[
  {
    "path": ".gitattributes",
    "content": "*.t linguist-language=Text\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "This place is for bug reports and development discussions only. For general questions and\ndiscussions, please join the openresty-en mailing list instead: https://openresty.org/en/community.html\n\nEnsure you have provided the following details while reporting a problem:\n\n* The exact version of the related software, including but not limited to the OpenResty version\n(if any), the NGINX core version, the `ngx_lua` module version,\nand your operating system version.\n* A minimal and standalone test case that others can easily run on their side and\nreproduce the issue you are seeing.\n* Do not simply say \"something is broken\" or \"something does not work\". Always provide\nas much details as possible. Always describe the symptoms and your expected results.\n* You can (temporarily) enable the nginx debugging logs to see the internal workings\nof NGINX in your nginx''s `error.log` file. See http://nginx.org/en/docs/debugging_log.html\nThe same instructions apply equally well to OpenResty.\n* If you are seeing crashes, please provide the full backtrace for the crash. See\nhttps://www.nginx.com/resources/wiki/start/topics/tutorials/debugging/#core-dump\nfor more details.\n\nPlease, do not use Chinese here. This place is considered English only. If you\nreally want to use Chinese, please join and post to the openresty (Chinese)\nmailing list instead. Please see https://openresty.org/en/community.html Thanks for\nyour cooperation.\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "I hereby granted the copyright of the changes in this pull request\nto the authors of this lua-nginx-module project.\n"
  },
  {
    "path": ".github/workflows/build_and_test.yml",
    "content": "name: Build and test\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  test:\n    name: ${{ matrix.name }}\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - name: \"nginx 1.29.4 + OpenSSL\"\n            NGINX_VERSION: \"1.29.4\"\n            TEST_NGINX_TIMEOUT: \"5\"\n\n          - name: \"nginx 1.29.4 + OpenSSL + HTTP/2\"\n            NGINX_VERSION: \"1.29.4\"\n            TEST_NGINX_TIMEOUT: \"5\"\n            TEST_NGINX_USE_HTTP2: \"1\"\n\n          - name: \"nginx 1.29.4 + OpenSSL + HTTP/3\"\n            NGINX_VERSION: \"1.29.4\"\n            TEST_NGINX_USE_HTTP3: \"1\"\n            TEST_NGINX_QUIC_IDLE_TIMEOUT: \"3\"\n\n          - name: \"nginx 1.29.4 + BoringSSL + HTTP/3\"\n            NGINX_VERSION: \"1.29.4\"\n            BORINGSSL: \"1\"\n            TEST_NGINX_USE_HTTP3: \"1\"\n            TEST_NGINX_QUIC_IDLE_TIMEOUT: \"3\"\n\n    services:\n      redis:\n        image: redis:7-alpine\n        ports:\n          - 6379:6379\n      mysql:\n        image: mysql:8.0\n        env:\n          MYSQL_ALLOW_EMPTY_PASSWORD: yes\n        ports:\n          - 3306:3306\n        options: >-\n          --health-cmd=\"mysqladmin ping --silent\"\n          --health-interval=10s\n          --health-timeout=5s\n          --health-retries=10\n\n    env:\n      JOBS: 3\n      NGX_BUILD_JOBS: 3\n      CC: gcc\n      DRIZZLE_VER: \"2011.07.21\"\n      LUAJIT_PREFIX: /opt/luajit21\n      LUAJIT_LIB: /opt/luajit21/lib\n      LUAJIT_INC: /opt/luajit21/include/luajit-2.1\n      LUA_INCLUDE_DIR: /opt/luajit21/include/luajit-2.1\n      PCRE2_PREFIX: /usr/local/openresty/pcre2\n      PCRE2_LIB: /usr/local/openresty/pcre2/lib\n      PCRE2_INC: /usr/local/openresty/pcre2/include\n      OPENSSL_PREFIX: /usr/local/openresty/openssl3\n      OPENSSL_LIB: /usr/local/openresty/openssl3/lib\n      OPENSSL_INC: /usr/local/openresty/openssl3/include\n      LIBDRIZZLE_PREFIX: /opt/drizzle\n      LIBDRIZZLE_INC: /opt/drizzle/include/libdrizzle-1.0\n      LIBDRIZZLE_LIB: /opt/drizzle/lib\n      TEST_NGINX_SLEEP: \"0.006\"\n      TEST_NGINX_SKIP_COSOCKET_LOG_TEST: \"1\"\n      MALLOC_PERTURB_: \"9\"\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Install system packages\n        run: |\n          sudo apt-get update -q\n          sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \\\n            ack axel cpanminus \\\n            libtest-base-perl libtext-diff-perl liburi-perl libwww-perl \\\n            libtest-longstring-perl liblist-moreutils-perl \\\n            libgd-dev time cmake libunwind-dev wget \\\n            libbrotli1 lsb-release gnupg ca-certificates dnsutils\n\n      - name: Install Perl test modules\n        run: |\n          /usr/bin/env perl $(command -v cpanm) --sudo --notest \\\n            Test::Nginx IPC::Run Test2::Util > build.log 2>&1 || (cat build.log && exit 1)\n\n      - name: Add OpenResty apt repository\n        run: |\n          wget -qO- https://openresty.org/package/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/openresty.gpg > /dev/null\n          echo \"deb [signed-by=/usr/share/keyrings/openresty.gpg] https://openresty.org/package/ubuntu $(lsb_release -sc) main\" \\\n            | sudo tee /etc/apt/sources.list.d/openresty.list\n          sudo apt-get update -q\n          sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \\\n            openresty-pcre2 openresty-openssl3 openresty-pcre2-dev openresty-openssl3-dev\n\n      - name: Cache download directory\n        uses: actions/cache@v4\n        with:\n          path: download-cache\n          key: download-cache-${{ runner.os }}-drizzle-${{ env.DRIZZLE_VER }}\n\n      - name: Source code style check\n        run: |\n          ! grep -n -P '(?<=.{80}).+' --color $(find src -name '*.c') $(find . -name '*.h') \\\n            || (echo \"ERROR: Found C source lines exceeding 80 columns.\" >&2; exit 1)\n          ! grep -n -P '\\t+' --color $(find src -name '*.c') $(find . -name '*.h') \\\n            || (echo \"ERROR: Cannot use tabs.\" >&2; exit 1)\n\n      - name: Clone dependency repositories\n        run: |\n          # Sibling repos — build scripts reference them via $root/../<name>\n          git clone --depth=1 https://github.com/openresty/openresty.git             ../openresty\n          git clone --depth=1 https://github.com/openresty/no-pool-nginx.git         ../no-pool-nginx\n          git clone --depth=1 https://github.com/openresty/lua-upstream-nginx-module.git ../lua-upstream-nginx-module\n          git clone --depth=1 https://github.com/openresty/echo-nginx-module.git         ../echo-nginx-module\n          git clone --depth=1 https://github.com/openresty/nginx-eval-module.git         ../nginx-eval-module\n          git clone --depth=1 https://github.com/simpl/ngx_devel_kit.git                 ../ndk-nginx-module\n          git clone --depth=1 https://github.com/FRiCKLE/ngx_coolkit.git                 ../coolkit-nginx-module\n          git clone --depth=1 https://github.com/openresty/headers-more-nginx-module.git ../headers-more-nginx-module\n          git clone --depth=1 https://github.com/openresty/drizzle-nginx-module.git      ../drizzle-nginx-module\n          git clone --depth=1 https://github.com/openresty/set-misc-nginx-module.git     ../set-misc-nginx-module\n          git clone --depth=1 https://github.com/openresty/memc-nginx-module.git         ../memc-nginx-module\n          git clone --depth=1 https://github.com/openresty/rds-json-nginx-module.git     ../rds-json-nginx-module\n          git clone --depth=1 https://github.com/openresty/srcache-nginx-module.git      ../srcache-nginx-module\n          git clone --depth=1 https://github.com/openresty/redis2-nginx-module.git       ../redis2-nginx-module\n          git clone --depth=1 https://github.com/openresty/lua-resty-core.git            ../lua-resty-core\n          git clone --depth=1 https://github.com/openresty/lua-resty-lrucache.git        ../lua-resty-lrucache\n          git clone --depth=1 https://github.com/openresty/lua-resty-mysql.git           ../lua-resty-mysql\n          git clone --depth=1 https://github.com/spacewander/lua-resty-rsa.git           ../lua-resty-rsa\n          git clone --depth=1 https://github.com/openresty/lua-resty-string.git          ../lua-resty-string\n          git clone --depth=1 https://github.com/openresty/stream-lua-nginx-module.git   ../stream-lua-nginx-module\n\n          # Local repos — used directly from the working directory\n          git clone --depth=1 https://github.com/openresty/test-nginx.git\n          git clone --depth=1 https://github.com/openresty/openresty-devel-utils.git\n          git clone --depth=1 https://github.com/openresty/mockeagain.git\n          git clone --depth=1 https://github.com/openresty/lua-cjson.git lua-cjson\n          git clone -b v2.1-agentzh --depth=1 https://github.com/openresty/luajit2.git luajit2\n\n      - name: Download prebuilt tarballs\n        run: |\n          mkdir -p download-cache\n          if [ ! -f download-cache/drizzle7-$DRIZZLE_VER.tar.gz ]; then\n            wget -P download-cache \\\n              https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/drizzle7-$DRIZZLE_VER.tar.gz\n          fi\n          wget https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/boringssl-20230902-x64-focal.tar.gz\n          wget https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/curl-h3-x64-focal.tar.gz\n\n      - name: Install curl-h3\n        run: sudo tar -C / -xf curl-h3-x64-focal.tar.gz\n\n      - name: Start memcached (native, UDP enabled)\n        run: |\n          sudo apt-get install -y --no-install-recommends memcached\n          sudo systemctl stop memcached\n          # Ubuntu's default /etc/memcached.conf has \"-U 0\" which disables UDP.\n          # Start manually with UDP on port 11211 so udp-socket tests pass.\n          memcached -d -l 127.0.0.1 -p 11211 -U 11211 -m 64\n          sudo ss -lntup | grep 11211\n\n\n      - name: Set up MySQL test database\n        run: |\n          mysql -h 127.0.0.1 -P 3306 -uroot \\\n            -e \"CREATE DATABASE ngx_test; \\\n                CREATE USER 'ngx_test'@'%' IDENTIFIED WITH mysql_native_password BY 'ngx_test'; \\\n                GRANT ALL ON ngx_test.* TO 'ngx_test'@'%'; \\\n                FLUSH PRIVILEGES;\"\n\n      - name: Set up network rules\n        run: |\n          sudo iptables -I OUTPUT 1 -p udp --dport 10086 -j REJECT\n          sudo iptables -I OUTPUT   -p tcp --dst 127.0.0.2 --dport 12345 -j DROP\n          sudo iptables -I OUTPUT   -p udp --dst 127.0.0.2 --dport 12345 -j DROP\n          sudo ip route add prohibit 0.0.0.1/32\n          sudo sysctl -w kernel.pid_max=10000\n\n      - name: Build LuaJIT\n        run: |\n          cd luajit2\n          make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC \\\n            XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT -msse4.2' \\\n            > build.log 2>&1 || (cat build.log && exit 1)\n          sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1)\n\n      - name: Build drizzle7\n        run: |\n          tar xzf download-cache/drizzle7-$DRIZZLE_VER.tar.gz\n          cd drizzle7-$DRIZZLE_VER\n          ./configure --prefix=$LIBDRIZZLE_PREFIX --without-server \\\n            > build.log 2>&1 || (cat build.log && exit 1)\n          make libdrizzle-1.0 -j$JOBS > build.log 2>&1 || (cat build.log && exit 1)\n          sudo make install-libdrizzle-1.0 > build.log 2>&1 || (cat build.log && exit 1)\n\n      - name: Build mockeagain\n        run: cd mockeagain && make CC=$CC -j$JOBS\n\n      - name: Build lua-cjson\n        run: cd lua-cjson && make -j$JOBS && sudo make install\n\n      - name: Set up BoringSSL\n        if: matrix.BORINGSSL == '1'\n        run: |\n          sudo rm -fr $OPENSSL_PREFIX\n          sudo mkdir -p $OPENSSL_PREFIX\n          sudo tar -C $OPENSSL_PREFIX -xf boringssl-20230902-x64-focal.tar.gz --strip-components=1\n\n      - name: Build nginx\n        run: |\n          export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH\n          export PATH=$PWD/work/nginx/sbin:$PWD/openresty-devel-utils:/opt/curl-h3/bin:$PATH\n          export NGX_BUILD_CC=$CC\n          sh util/build-without-ssl.sh ${{ matrix.NGINX_VERSION }} > build.log 2>&1 || (cat build.log && exit 1)\n          sh util/build-with-dd.sh     ${{ matrix.NGINX_VERSION }} > build.log 2>&1 || (cat build.log && exit 1)\n          rm -fr buildroot\n          sh util/build.sh             ${{ matrix.NGINX_VERSION }} > build.log 2>&1 || (cat build.log && exit 1)\n          nginx -V\n          ldd $(which nginx) | grep -E 'luajit|ssl|pcre'\n\n      - name: Run releng check\n        run: |\n          export PATH=$PWD/work/nginx/sbin:$PWD/openresty-devel-utils:/opt/curl-h3/bin:$PATH\n          ngx-releng > check.txt || true\n          lines=$(wc -l check.txt | awk '{print $1}')\n          if [ \"$lines\" -gt 5 ]; then cat check.txt; exit 1; fi\n\n      - name: Set matrix-specific test env vars\n        run: |\n          # Only export vars that have actual values — Perl's defined() treats\n          # empty strings as defined, which would cause false positives in tests\n          # like t/103-req-http-ver.t that branch on defined $ENV{TEST_NGINX_USE_HTTP3}.\n          [ -n \"${{ matrix.TEST_NGINX_TIMEOUT }}\" ] \\\n            && echo \"TEST_NGINX_TIMEOUT=${{ matrix.TEST_NGINX_TIMEOUT }}\" >> $GITHUB_ENV || true\n          [ -n \"${{ matrix.TEST_NGINX_USE_HTTP2 }}\" ] \\\n            && echo \"TEST_NGINX_USE_HTTP2=${{ matrix.TEST_NGINX_USE_HTTP2 }}\" >> $GITHUB_ENV || true\n          [ -n \"${{ matrix.TEST_NGINX_USE_HTTP3 }}\" ] \\\n            && echo \"TEST_NGINX_USE_HTTP3=${{ matrix.TEST_NGINX_USE_HTTP3 }}\" >> $GITHUB_ENV || true\n          [ -n \"${{ matrix.TEST_NGINX_QUIC_IDLE_TIMEOUT }}\" ] \\\n            && echo \"TEST_NGINX_QUIC_IDLE_TIMEOUT=${{ matrix.TEST_NGINX_QUIC_IDLE_TIMEOUT }}\" >> $GITHUB_ENV || true\n\n      - name: Run tests\n        run: |\n          export LD_LIBRARY_PATH=$LUAJIT_LIB:$PWD/mockeagain:$LD_LIBRARY_PATH\n          export PATH=$PWD/work/nginx/sbin:$PWD/openresty-devel-utils:/opt/curl-h3/bin:$PATH\n          export LD_PRELOAD=$PWD/mockeagain/mockeagain.so\n          export TEST_NGINX_HTTP3_CRT=$PWD/t/cert/http3/http3.crt\n          export TEST_NGINX_HTTP3_KEY=$PWD/t/cert/http3/http3.key\n          export TEST_NGINX_RESOLVER=8.8.4.4\n          dig +short myip.opendns.com @resolver1.opendns.com || exit 0\n          dig +short @$TEST_NGINX_RESOLVER openresty.org || exit 0\n          dig +short @$TEST_NGINX_RESOLVER agentzh.org || exit 0\n          python3 ./util/nc_server.py &\n          /usr/bin/env perl $(command -v prove) -I. -Itest-nginx/inc -Itest-nginx/lib -r t/\n"
  },
  {
    "path": ".github/workflows/semantic-pull-request.yml",
    "content": "name: \"Lint PR\"\n\non:\n  pull_request_target:\n    types:\n      - opened\n      - edited\n      - synchronize\n\njobs:\n  main:\n    name: Validate PR title\n    runs-on: ubuntu-latest\n    steps:\n      - uses: amannn/action-semantic-pull-request@v4\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          # Configure which types are allowed.\n          # Default: https://github.com/commitizen/conventional-commit-types\n          types: |\n            bugfix # bug fixes\n            change # backward incompatible changes\n            doc # documentation changes including code comments\n            editor # code editor related configurations\n            feature # implementing a new feature\n            optimize # performance optimizations\n            refactor # code refactoring and other code rearrangement\n            style # coding style changes\n            tests # test suite changes\n"
  },
  {
    "path": ".gitignore",
    "content": "build/\nwork/\ntags\ncscope.*\n*.mobi\ngenmobi.sh\n.libs\n*.swp\n*.slo\n*.la\n*.swo\n*.lo\n*~\n*.o\nprint.txt\n.rsync\n*.tar.gz\ndist\nbuild[789]\nbuild\ntags\nupdate-readme\n*.tmp\ntest/Makefile\ntest/blib\ntest.sh\nt.sh\nt/t.sh\nt/servroot*\ntest/t/servroot/\nreleng\nreset\n*.t_\nsrc/handler.h\nsrc/util.c\nsrc/module.h\nsrc/module.c\nsrc/drizzle.c\nsrc/processor.h\nsrc/handler.c\nsrc/util.h\nsrc/drizzle.h\nsrc/processor.c\nsrc/output.c\nsrc/output.h\nlibdrizzle\nctags\nsrc/stream.h\nnginx\nkeepalive\nreindex\nsrc/keepalive.c\nsrc/keepalive.h\nsrc/checker.h\nsrc/checker.c\nsrc/quoting.h\nsrc/quoting.c\nsrc/module.h\nsrc/module.c\nsrc/util.h\nsrc/util.c\nsrc/processor.h\nsrc/processor.c\nsrc/rds.h\nsrc/utils.h\nsrc/handler.c\nsrc/handler.h\nutil/bench\n*.html\ntrace.out*\ntry.sh\nsrc/cache.c\nsrc/cache.h\nsrc/common.h\nsrc/directive.c\nsrc/directive.h\nsrc/consts.[ch]\nsrc/contentby.[ch]\nsrc/pcrefix.[ch]\nsrc/util.c\nsrc/clfactory.c\nsrc/directive.c\nsrc/conf.h\nsrc/setby.h\nsrc/cache.h\nsrc/hook.c\nsrc/util.h\nsrc/hook.h\nsrc/common.h\nsrc/directive.h\nsrc/conf.c\nsrc/setby.c\nsrc/cache.c\nsrc/module.c\nsrc/clfactory.h\nsrc/capturefilter.[ch]\nsrc/contentby.c\npack\nb.sh\nsrc/in.[ch]\nsrc/out.[ch]\ngo\nall.sh\nsrc/accessby.[ch]\nsrc/rewriteby.[ch]\nsrc/patch.[ch]\nsrc/ndk.[ch]\nsrc/control.[ch]\nsrc/output.[ch]\nsrc/variable.[ch]\nsrc/string.[ch]\nsrc/misc.[ch]\nsrc/log.[ch]\nsrc/exception.[ch]\nsrc/subrequest.[ch]\nsrc/time.[ch]\nsrc/regex.[ch]\nsrc/ctx.[ch]\nsrc/args.[ch]\nsrc/headers.[ch]\nsrc/script.[ch]\nsrc/filter.[ch]\nsrc/shdict.[ch]\nsrc/body.[ch]\nsrc/uri.[ch]\nsrc/api.[ch]\nsrc/coroutine.[ch]\nsrc/logby.[ch]\nsrc/sleep.[ch]\na.patch\nall\nbuild1[0-9]\ng\nbuildroot/\nsrc/headerfilterby.[ch]\n*.patch\nanalyze\ntsock\na.c\ntest.lua\nbuild12\nERRORS\nsrc/bodyfilterby.[ch]\nsrc/tcp.[ch]\nsrc/initby.[ch]\nsrc/initworkerby.[ch]\nsrc/socket.[ch]\nsrc/udp.[ch]\nsrc/method.[ch]\ntre\nsrc/phase.[ch]\nsrc/probe.h\nsrc/uthread.[ch]\nsrc/timer.[ch]\nsrc/config.[ch]\nsrc/worker.[ch]\nsrc/certby.[ch]\nsrc/storeby.[ch]\nsrc/fetchby.[ch]\nsrc/ssl.[ch]\nsrc/ocsp.c\nsrc/lex.[ch]\nsrc/balancer.[ch]\nsrc/semaphore.[ch]\n*.plist\nlua\nttimer\nMakefile\ntsubreq\ntthread\naddr2line\nhup\ntheaders\nsrc/ngx_http_lua_autoconf.h\nsrc/autoconf.h\nsrc/filters.c\nsrc/filters.h\nsrc/ringbuf.c\nsrc/ringbuf.h\nsrc/pipe.[ch]\n"
  },
  {
    "path": ".mergify.yml",
    "content": "---\npull_request_rules:\n  - name: warn on conflicts\n    conditions:\n      - conflict\n    actions:\n      comment:\n        message: This pull request is now in conflict :(\n      label:\n        add:\n          - conflict\n  - name: remove conflict label if not needed\n    conditions:\n      - -conflict\n    actions:\n      label:\n        remove:\n          - conflict\n  - name: add label needs-test-cases\n    conditions:\n      - files~=^src/\n      - -files~=^t/\n    actions:\n      label:\n        add:\n          - needs-test-cases\n  - name: remove label needs-test-cases\n    conditions:\n      - label=needs-test-cases\n      - files~=^src/\n      - files~=^t/\n    actions:\n      label:\n        remove:\n          - needs-test-cases\n  - name: add label could-be-merged\n    conditions:\n      - \"#approved-reviews-by>=2\"\n      - status-success=Travis CI - Pull Request\n    actions:\n      label:\n        add:\n          - could-be-merged\n"
  },
  {
    "path": "README.markdown",
    "content": "Name\n====\n\nngx_http_lua_module - Embed the power of Lua into Nginx HTTP Servers.\n\nThis module is a core component of [OpenResty](https://openresty.org). If you are using this module,\nthen you are essentially using OpenResty :)\n\n*This module is not distributed with the Nginx source.* See\n[the installation instructions](#installation).\n\nTable of Contents\n=================\n\n* [Name](#name)\n* [Status](#status)\n* [Version](#version)\n* [Videos](#videos)\n* [Synopsis](#synopsis)\n* [Description](#description)\n* [Typical Uses](#typical-uses)\n* [Nginx Compatibility](#nginx-compatibility)\n* [Installation](#installation)\n    * [Building as a dynamic module](#building-as-a-dynamic-module)\n    * [C Macro Configurations](#c-macro-configurations)\n* [Community](#community)\n    * [English Mailing List](#english-mailing-list)\n    * [Chinese Mailing List](#chinese-mailing-list)\n* [Code Repository](#code-repository)\n* [Bugs and Patches](#bugs-and-patches)\n* [LuaJIT bytecode support](#luajit-bytecode-support)\n* [System Environment Variable Support](#system-environment-variable-support)\n* [HTTP 1.0 support](#http-10-support)\n* [Statically Linking Pure Lua Modules](#statically-linking-pure-lua-modules)\n* [Data Sharing within an Nginx Worker](#data-sharing-within-an-nginx-worker)\n* [Known Issues](#known-issues)\n    * [TCP socket connect operation issues](#tcp-socket-connect-operation-issues)\n    * [Lua Coroutine Yielding/Resuming](#lua-coroutine-yieldingresuming)\n    * [Lua Variable Scope](#lua-variable-scope)\n    * [Locations Configured by Subrequest Directives of Other Modules](#locations-configured-by-subrequest-directives-of-other-modules)\n    * [Cosockets Not Available Everywhere](#cosockets-not-available-everywhere)\n    * [Special Escaping Sequences](#special-escaping-sequences)\n    * [Mixing with SSI Not Supported](#mixing-with-ssi-not-supported)\n    * [SPDY Mode Not Fully Supported](#spdy-mode-not-fully-supported)\n    * [Missing data on short circuited requests](#missing-data-on-short-circuited-requests)\n* [TODO](#todo)\n* [Changes](#changes)\n* [Build And Test](#build-and-test)\n* [Test Suite](#test-suite)\n* [Copyright and License](#copyright-and-license)\n* [See Also](#see-also)\n* [Directives](#directives)\n* [Nginx API for Lua](#nginx-api-for-lua)\n* [Obsolete Sections](#obsolete-sections)\n    * [Special PCRE Sequences](#special-pcre-sequences)\n    * [Lua/LuaJIT bytecode support](#lualuajit-bytecode-support)\n\nStatus\n======\n\nProduction ready.\n\nVersion\n=======\n\nThis document describes ngx_lua\n[v0.10.29](https://github.com/openresty/lua-nginx-module/tags), which was released\non Oct 24, 2025.\n\nVideos\n======\n\n* YouTube video \"[Hello World HTTP Example with OpenResty/Lua](https://youtu.be/eSfYLvVQMxw)\"\n\n    [![Hello World HTTP Example with OpenResty/Lua](https://img.youtube.com/vi/eSfYLvVQMxw/0.jpg)](https://youtu.be/eSfYLvVQMxw)\n\n* YouTube video \"[Write Your Own Lua Modules in OpenResty/Nginx Applications](https://youtu.be/vfYxOMl5LVY)\"\n\n    [![Write Your Own Lua Modules in OpenResty/Nginx Applications](https://img.youtube.com/vi/vfYxOMl5LVY/0.jpg)](https://youtu.be/vfYxOMl5LVY)\n\n* YouTube video \"[OpenResty's resty Command-Line Utility Demo](https://youtu.be/L1c7aw4mSOo)\"\n\n    [![OpenResty's resty Command-Line Utility Demo](https://img.youtube.com/vi/L1c7aw4mSOo/0.jpg)](https://youtu.be/L1c7aw4mSOo)\n\n* YouTube video \"[Measure Execution Time of Lua Code Correctly in OpenResty](https://youtu.be/VkRYW_qLoME)\"\n\n    [![Measure Execution Time of Lua Code Correctly in OpenResty](https://img.youtube.com/vi/VkRYW_qLoME/0.jpg)](https://youtu.be/VkRYW_qLoME)\n\n* YouTube video \"[Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup](https://youtu.be/EP7c0BM2yNo)\"\n\n    [![Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup](https://img.youtube.com/vi/EP7c0BM2yNo/0.jpg)](https://youtu.be/EP7c0BM2yNo)\n\nYou are welcome to subscribe to our [official YouTube channel, OpenResty](https://www.youtube.com/channel/UCXVmwF-UCScv2ftsGoMqxhw).\n\n[Back to TOC](#table-of-contents)\n\nSynopsis\n========\n```nginx\n\n # set search paths for pure Lua external libraries (';;' is the default path):\n lua_package_path '/foo/bar/?.lua;/blah/?.lua;;';\n\n # set search paths for Lua external libraries written in C (can also use ';;'):\n lua_package_cpath '/bar/baz/?.so;/blah/blah/?.so;;';\n\n server {\n     location /lua_content {\n         # MIME type determined by default_type:\n         default_type 'text/plain';\n\n         content_by_lua_block {\n             ngx.say('Hello,world!')\n         }\n     }\n\n     location /nginx_var {\n         # MIME type determined by default_type:\n         default_type 'text/plain';\n\n         # try access /nginx_var?a=hello,world\n         content_by_lua_block {\n             ngx.say(ngx.var.arg_a)\n         }\n     }\n\n     location = /request_body {\n         client_max_body_size 50k;\n         client_body_buffer_size 50k;\n\n         content_by_lua_block {\n             ngx.req.read_body()  -- explicitly read the req body\n             local data = ngx.req.get_body_data()\n             if data then\n                 ngx.say(\"body data:\")\n                 ngx.print(data)\n                 return\n             end\n\n             -- body may get buffered in a temp file:\n             local file = ngx.req.get_body_file()\n             if file then\n                 ngx.say(\"body is in file \", file)\n             else\n                 ngx.say(\"no body found\")\n             end\n         }\n     }\n\n     # transparent non-blocking I/O in Lua via subrequests\n     # (well, a better way is to use cosockets)\n     location = /lua {\n         # MIME type determined by default_type:\n         default_type 'text/plain';\n\n         content_by_lua_block {\n             local res = ngx.location.capture(\"/some_other_location\")\n             if res then\n                 ngx.say(\"status: \", res.status)\n                 ngx.say(\"body:\")\n                 ngx.print(res.body)\n             end\n         }\n     }\n\n     location = /foo {\n         rewrite_by_lua_block {\n             res = ngx.location.capture(\"/memc\",\n                 { args = { cmd = \"incr\", key = ngx.var.uri } }\n             )\n         }\n\n         proxy_pass http://blah.blah.com;\n     }\n\n     location = /mixed {\n         rewrite_by_lua_file /path/to/rewrite.lua;\n         access_by_lua_file /path/to/access.lua;\n         content_by_lua_file /path/to/content.lua;\n     }\n\n     # use nginx var in code path\n     # CAUTION: contents in nginx var must be carefully filtered,\n     # otherwise there'll be great security risk!\n     location ~ ^/app/([-_a-zA-Z0-9/]+) {\n         set $path $1;\n         content_by_lua_file /path/to/lua/app/root/$path.lua;\n     }\n\n     location / {\n        client_max_body_size 100k;\n        client_body_buffer_size 100k;\n\n        access_by_lua_block {\n            -- check the client IP address is in our black list\n            if ngx.var.remote_addr == \"132.5.72.3\" then\n                ngx.exit(ngx.HTTP_FORBIDDEN)\n            end\n\n            -- check if the URI contains bad words\n            if ngx.var.uri and\n                   string.match(ngx.var.request_body, \"evil\")\n            then\n                return ngx.redirect(\"/terms_of_use.html\")\n            end\n\n            -- tests passed\n        }\n\n        # proxy_pass/fastcgi_pass/etc settings\n     }\n }\n```\n\n[Back to TOC](#table-of-contents)\n\nDescription\n===========\n\nThis module embeds [LuaJIT 2.0/2.1](https://luajit.org/luajit.html) into Nginx.\nIt is a core component of [OpenResty](https://openresty.org). If you are using\nthis module, then you are essentially using OpenResty.\n\nSince version `v0.10.16` of this module, the standard Lua\ninterpreter (also known as \"PUC-Rio Lua\") is not supported anymore. This\ndocument interchangeably uses the terms \"Lua\" and \"LuaJIT\" to refer to the\nLuaJIT interpreter.\n\nBy leveraging Nginx's subrequests, this module allows the integration of the\npowerful Lua threads (known as Lua \"coroutines\") into the Nginx event model.\n\nUnlike [Apache's mod_lua](https://httpd.apache.org/docs/trunk/mod/mod_lua.html)\nand [Lighttpd's mod_magnet](http://redmine.lighttpd.net/wiki/1/Docs:ModMagnet),\nLua code executed using this module can be *100% non-blocking* on network\ntraffic as long as the [Nginx API for Lua](#nginx-api-for-lua) provided by\nthis module is used to handle requests to upstream services such as MySQL,\nPostgreSQL, Memcached, Redis, or upstream HTTP web services.\n\nAt least the following Lua libraries and Nginx modules can be used with this\nmodule:\n\n* [lua-resty-memcached](https://github.com/openresty/lua-resty-memcached)\n* [lua-resty-mysql](https://github.com/openresty/lua-resty-mysql)\n* [lua-resty-redis](https://github.com/openresty/lua-resty-redis)\n* [lua-resty-dns](https://github.com/openresty/lua-resty-dns)\n* [lua-resty-upload](https://github.com/openresty/lua-resty-upload)\n* [lua-resty-websocket](https://github.com/openresty/lua-resty-websocket)\n* [lua-resty-lock](https://github.com/openresty/lua-resty-lock)\n* [lua-resty-logger-socket](https://github.com/cloudflare/lua-resty-logger-socket)\n* [lua-resty-lrucache](https://github.com/openresty/lua-resty-lrucache)\n* [lua-resty-string](https://github.com/openresty/lua-resty-string)\n* [ngx_memc](http://github.com/openresty/memc-nginx-module)\n* [ngx_postgres](https://github.com/FRiCKLE/ngx_postgres)\n* [ngx_redis2](http://github.com/openresty/redis2-nginx-module)\n* [ngx_redis](http://wiki.nginx.org/HttpRedisModule)\n* [ngx_proxy](http://nginx.org/en/docs/http/ngx_http_proxy_module.html)\n* [ngx_fastcgi](http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html)\n\nAlmost any Nginx modules can be used with this ngx_lua module by means of\n[ngx.location.capture](#ngxlocationcapture) or\n[ngx.location.capture_multi](#ngxlocationcapture_multi) but it is\nrecommended to use those `lua-resty-*` libraries instead of creating\nsubrequests to access the Nginx upstream modules because the former is usually\nmuch more flexible and memory-efficient.\n\nThe Lua interpreter (also known as \"Lua State\" or \"LuaJIT VM instance\") is\nshared across all the requests in a single Nginx worker process to minimize\nmemory use. Request contexts are segregated using lightweight Lua coroutines.\n\nLoaded Lua modules persist in the Nginx worker process level resulting in a\nsmall memory footprint in Lua even when under heavy loads.\n\nThis module is plugged into Nginx's \"http\" subsystem so it can only speak\ndownstream communication protocols in the HTTP family (HTTP 0.9/1.0/1.1/2.0,\nWebSockets, etc...).  If you want to do generic TCP communications with the\ndownstream clients, then you should use the\n[ngx_stream_lua](https://github.com/openresty/stream-lua-nginx-module#readme)\nmodule instead, which offers a compatible Lua API.\n\n[Back to TOC](#table-of-contents)\n\nTypical Uses\n============\n\nJust to name a few:\n\n* Mashup'ing and processing outputs of various Nginx upstream outputs (proxy, drizzle, postgres, redis, memcached, etc.) in Lua,\n* doing arbitrarily complex access control and security checks in Lua before requests actually reach the upstream backends,\n* manipulating response headers in an arbitrary way (by Lua)\n* 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,\n* coding up arbitrarily complex web applications in a content handler using synchronous but still non-blocking access to the database backends and other storage,\n* doing very complex URL dispatch in Lua at rewrite phase,\n* using Lua to implement advanced caching mechanism for Nginx's subrequests and arbitrary locations.\n\nThe possibilities are unlimited as the module allows bringing together various\nelements within Nginx as well as exposing the power of the Lua language to the\nuser. The module provides the full flexibility of scripting while offering\nperformance levels comparable with native C language programs both in terms of\nCPU time as well as memory footprint thanks to LuaJIT 2.x.\n\nOther scripting language implementations typically struggle to match this\nperformance level.\n\n[Back to TOC](#table-of-contents)\n\nNginx Compatibility\n===================\n\nThe latest version of this module is compatible with the following versions of Nginx:\n\n* 1.29.x  (last tested: 1.29.2)\n* 1.27.x  (last tested: 1.27.1)\n* 1.25.x  (last tested: 1.25.1)\n* 1.21.x  (last tested: 1.21.4)\n* 1.19.x  (last tested: 1.19.3)\n* 1.17.x  (last tested: 1.17.8)\n* 1.15.x  (last tested: 1.15.8)\n* 1.14.x\n* 1.13.x  (last tested: 1.13.6)\n* 1.12.x\n* 1.11.x  (last tested: 1.11.2)\n* 1.10.x\n* 1.9.x (last tested: 1.9.15)\n* 1.8.x\n* 1.7.x (last tested: 1.7.10)\n* 1.6.x\n\nNginx cores older than 1.6.0 (exclusive) are *not* supported.\n\n[Back to TOC](#table-of-contents)\n\nInstallation\n============\n\nIt is *highly* recommended to use [OpenResty releases](https://openresty.org)\nwhich bundle Nginx, ngx_lua (this module), LuaJIT, as well as other powerful\ncompanion Nginx modules and Lua libraries.\n\nIt is discouraged to build this module with Nginx yourself since it is tricky\nto set up exactly right.\n\nNote that Nginx, LuaJIT, and OpenSSL official releases have various limitations\nand long-standing bugs that can cause some of this module's features to be\ndisabled, not work properly, or run slower. Official OpenResty releases are\nrecommended because they bundle [OpenResty's optimized LuaJIT 2.1 fork](https://github.com/openresty/luajit2) and\n[Nginx/OpenSSL\npatches](https://github.com/openresty/openresty/tree/master/patches).\n\nAlternatively, ngx_lua can be manually compiled into Nginx:\n\n1. LuaJIT can be downloaded from the [latest release of OpenResty's LuaJIT fork](https://github.com/openresty/luajit2/releases). The official LuaJIT 2.x releases are also supported, although performance will be significantly lower for reasons elaborated above\n1. Download the latest version of the ngx_devel_kit (NDK) module [HERE](https://github.com/simplresty/ngx_devel_kit/tags)\n1. Download the latest version of ngx_lua [HERE](https://github.com/openresty/lua-nginx-module/tags)\n1. Download the latest supported version of Nginx [HERE](https://nginx.org/) (See [Nginx Compatibility](#nginx-compatibility))\n1. Download the latest version of the lua-resty-core [HERE](https://github.com/openresty/lua-resty-core)\n1. Download the latest version of the lua-resty-lrucache [HERE](https://github.com/openresty/lua-resty-lrucache)\n\nBuild the source with this module:\n\n```bash\n\n wget 'https://openresty.org/download/nginx-1.19.3.tar.gz'\n tar -xzvf nginx-1.19.3.tar.gz\n cd nginx-1.19.3/\n\n # tell nginx's build system where to find LuaJIT 2.0:\n export LUAJIT_LIB=/path/to/luajit/lib\n export LUAJIT_INC=/path/to/luajit/include/luajit-2.0\n\n # tell nginx's build system where to find LuaJIT 2.1:\n export LUAJIT_LIB=/path/to/luajit/lib\n export LUAJIT_INC=/path/to/luajit/include/luajit-2.1\n\n # Here we assume Nginx is to be installed under /opt/nginx/.\n ./configure --prefix=/opt/nginx \\\n         --with-ld-opt=\"-Wl,-rpath,/path/to/luajit/lib\" \\\n         --add-module=/path/to/ngx_devel_kit \\\n         --add-module=/path/to/lua-nginx-module\n\n # Note that you may also want to add `./configure` options which are used in your\n # current nginx build.\n # You can get usually those options using command nginx -V\n\n # you can change the parallelism number 2 below to fit the number of spare CPU cores in your\n # machine.\n make -j2\n make install\n\n # Note that this version of lug-nginx-module not allow to set `lua_load_resty_core off;` any more.\n # So, you have to install `lua-resty-core` and `lua-resty-lrucache` manually as below.\n\n cd lua-resty-core\n make install PREFIX=/opt/nginx\n cd lua-resty-lrucache\n make install PREFIX=/opt/nginx\n\n # add necessary `lua_package_path` directive to `nginx.conf`, in the http context\n\n lua_package_path \"/opt/nginx/lib/lua/?.lua;;\";\n```\n\n[Back to TOC](#table-of-contents)\n\nBuilding as a dynamic module\n----------------------------\n\nStarting from NGINX 1.9.11, you can also compile this module as a dynamic module, by using the `--add-dynamic-module=PATH` option instead of `--add-module=PATH` on the\n`./configure` command line above. And then you can explicitly load the module in your `nginx.conf` via the [load_module](https://nginx.org/en/docs/ngx_core_module.html#load_module)\ndirective, for example,\n\n```nginx\n\n load_module /path/to/modules/ndk_http_module.so;  # assuming NDK is built as a dynamic module too\n load_module /path/to/modules/ngx_http_lua_module.so;\n```\n\n[Back to TOC](#table-of-contents)\n\nC Macro Configurations\n----------------------\n\nWhile building this module either via OpenResty or with the Nginx core, you can define the following C macros via the C compiler options:\n\n* `NGX_LUA_USE_ASSERT`\n\tWhen 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.\n* `NGX_LUA_ABORT_AT_PANIC`\n\tWhen 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.\n\nTo enable one or more of these macros, just pass extra C compiler options to the `./configure` script of either Nginx or OpenResty. For instance,\n\n\n    ./configure --with-cc-opt=\"-DNGX_LUA_USE_ASSERT -DNGX_LUA_ABORT_AT_PANIC\"\n\n\n[Back to TOC](#table-of-contents)\n\nCommunity\n=========\n\n[Back to TOC](#table-of-contents)\n\nEnglish Mailing List\n--------------------\n\nThe [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers.\n\n[Back to TOC](#table-of-contents)\n\nChinese Mailing List\n--------------------\n\nThe [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers.\n\n[Back to TOC](#table-of-contents)\n\nCode Repository\n===============\n\nThe code repository of this project is hosted on GitHub at\n[openresty/lua-nginx-module](https://github.com/openresty/lua-nginx-module).\n\n[Back to TOC](#table-of-contents)\n\nBugs and Patches\n================\n\nPlease submit bug reports, wishlists, or patches by\n\n1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-nginx-module/issues),\n1. or posting to the [OpenResty community](#community).\n\n[Back to TOC](#table-of-contents)\n\nLuaJIT bytecode support\n=======================\n\nWatch YouTube video \"[Measure Execution Time of Lua Code Correctly in OpenResty](https://youtu.be/VkRYW_qLoME)\"\n\n[![Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup](https://img.youtube.com/vi/EP7c0BM2yNo/0.jpg)](https://youtu.be/EP7c0BM2yNo)\n\nAs 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:\n\n```bash\n\n /path/to/luajit/bin/luajit -b /path/to/input_file.lua /path/to/output_file.ljbc\n```\n\nThe `-bg` option can be used to include debug information in the LuaJIT bytecode file:\n\n```bash\n\n /path/to/luajit/bin/luajit -bg /path/to/input_file.lua /path/to/output_file.ljbc\n```\n\nPlease refer to the official LuaJIT documentation on the `-b` option for more details:\n\n<https://luajit.org/running.html#opt_b>\n\nNote that the bytecode files generated by LuaJIT 2.1 is *not* compatible with\nLuaJIT 2.0, and vice versa. The support for LuaJIT 2.1 bytecode was first added\nin ngx_lua v0.9.3.\n\nAttempts to load standard Lua 5.1 bytecode files into ngx_lua instances linked\nto LuaJIT 2.0/2.1 (or vice versa) will result in an Nginx error message such as\nthe one below:\n\n\n    [error] 13909#0: *1 failed to load Lua inlined code: bad byte-code header in /path/to/test_file.luac\n\n\nLoading bytecode files via the Lua primitives like `require` and\n`dofile` should always work as expected.\n\n[Back to TOC](#table-of-contents)\n\nSystem Environment Variable Support\n===================================\n\nIf you want to access the system environment variable, say, `foo`, in Lua via the standard Lua API [os.getenv](https://www.lua.org/manual/5.1/manual.html#pdf-os.getenv), then you should also list this environment variable name in your `nginx.conf` file via the [env directive](https://nginx.org/en/docs/ngx_core_module.html#env). For example,\n\n```nginx\n\n env foo;\n```\n\n[Back to TOC](#table-of-contents)\n\nHTTP 1.0 support\n================\n\nThe HTTP 1.0 protocol does not support chunked output and requires an explicit `Content-Length` header when the response body is not empty in order to support the HTTP 1.0 keep-alive.\nSo when a HTTP 1.0 request is made and the [lua_http10_buffering](#lua_http10_buffering) directive is turned `on`, ngx_lua will buffer the\noutput of [ngx.say](#ngxsay) and [ngx.print](#ngxprint) calls and also postpone sending response headers until all the response body output is received.\nAt that time ngx_lua can calculate the total length of the body and construct a proper `Content-Length` header to return to the HTTP 1.0 client.\nIf the `Content-Length` response header is set in the running Lua code, however, this buffering will be disabled even if the [lua_http10_buffering](#lua_http10_buffering) directive is turned `on`.\n\nFor large streaming output responses, it is important to disable the [lua_http10_buffering](#lua_http10_buffering) directive to minimise memory usage.\n\nNote that common HTTP benchmark tools such as `ab` and `http_load` issue HTTP 1.0 requests by default.\nTo force `curl` to send HTTP 1.0 requests, use the `-0` option.\n\n[Back to TOC](#table-of-contents)\n\nStatically Linking Pure Lua Modules\n===================================\n\nWith LuaJIT 2.x, it is possible to statically link the bytecode of pure Lua\nmodules into the Nginx executable.\n\nYou can use the `luajit` executable to compile `.lua` Lua\nmodule files to `.o` object files containing the exported bytecode\ndata, and then link the `.o` files directly in your Nginx build.\n\nBelow is a trivial example to demonstrate this. Consider that we have the following `.lua` file named `foo.lua`:\n\n```lua\n\n -- foo.lua\n local _M = {}\n\n function _M.go()\n     print(\"Hello from foo\")\n end\n\n return _M\n```\n\nAnd then we compile this `.lua` file to `foo.o` file:\n\n```bash\n\n /path/to/luajit/bin/luajit -bg foo.lua foo.o\n```\n\nWhat matters here is the name of the `.lua` file, which determines how you use this module later on the Lua land. The file name `foo.o` does not matter at all except the `.o` file extension (which tells `luajit` what output format is used). If you want to strip the Lua debug information from the resulting bytecode, you can just specify the `-b` option above instead of `-bg`.\n\nThen when building Nginx or OpenResty, pass the `--with-ld-opt=\"foo.o\"` option to the `./configure` script:\n\n```bash\n\n ./configure --with-ld-opt=\"/path/to/foo.o\" ...\n```\n\nFinally, you can just do the following in any Lua code run by ngx_lua:\n\n```lua\n\n local foo = require \"foo\"\n foo.go()\n```\n\nAnd this piece of code no longer depends on the external `foo.lua` file any more because it has already been compiled into the `nginx` executable.\n\nIf you want to use dot in the Lua module name when calling `require`, as in\n\n```lua\n\n local foo = require \"resty.foo\"\n```\n\nthen you need to rename the `foo.lua` file to `resty_foo.lua` before compiling it down to a `.o` file with the `luajit` command-line utility.\n\nIt is important to use exactly the same version of LuaJIT when compiling `.lua` files to `.o` files as building nginx + ngx_lua. This is because the LuaJIT bytecode format may be incompatible between different LuaJIT versions. When the bytecode format is incompatible, you will see a Lua runtime error saying that the Lua module is not found.\n\nWhen you have multiple `.lua` files to compile and link, then just specify their `.o` files at the same time in the value of the `--with-ld-opt` option. For instance,\n\n```bash\n\n ./configure --with-ld-opt=\"/path/to/foo.o /path/to/bar.o\" ...\n```\n\nIf you have too many `.o` files, then it might not be feasible to name them all in a single command. In this case, you can build a static library (or archive) for your `.o` files, as in\n\n```bash\n\n ar rcus libmyluafiles.a *.o\n```\n\nthen you can link the `myluafiles` archive as a whole to your nginx executable:\n\n```bash\n\n ./configure \\\n     --with-ld-opt=\"-L/path/to/lib -Wl,--whole-archive -lmyluafiles -Wl,--no-whole-archive\"\n```\n\nwhere `/path/to/lib` is the path of the directory containing the `libmyluafiles.a` file. It should be noted that the linker option `--whole-archive` is required here because otherwise our archive will be skipped because no symbols in our archive are mentioned in the main parts of the nginx executable.\n\n[Back to TOC](#table-of-contents)\n\nData Sharing within an Nginx Worker\n===================================\n\nTo globally share data among all the requests handled by the same Nginx worker\nprocess, encapsulate the shared data into a Lua module, use the Lua\n`require` builtin to import the module, and then manipulate the\nshared data in Lua. This works because required Lua modules are loaded only\nonce and all coroutines will share the same copy of the module (both its code\nand data).\n\nNote that the use of global Lua variables is *strongly discouraged*, as it may\nlead to unexpected race conditions between concurrent requests.\n\nHere is a small example on sharing data within an Nginx worker via a Lua module:\n\n```lua\n\n -- mydata.lua\n local _M = {}\n\n local data = {\n     dog = 3,\n     cat = 4,\n     pig = 5,\n }\n\n function _M.get_age(name)\n     return data[name]\n end\n\n return _M\n```\n\nand then accessing it from `nginx.conf`:\n\n```nginx\n\n location /lua {\n     content_by_lua_block {\n         local mydata = require \"mydata\"\n         ngx.say(mydata.get_age(\"dog\"))\n     }\n }\n```\n\nThe `mydata` module in this example will only be loaded and run on the first request to the location `/lua`,\nand all subsequent requests to the same Nginx worker process will use the reloaded instance of the\nmodule as well as the same copy of the data in it, until a `HUP` signal is sent to the Nginx master process to force a reload.\nThis data sharing technique is essential for high performance Lua applications based on this module.\n\nNote that this data sharing is on a *per-worker* basis and not on a *per-server* basis. That is, when there are multiple Nginx worker processes under an Nginx master, data sharing cannot cross the process boundary between these workers.\n\nIt is usually recommended to share read-only data this way. You can also share changeable data among all the concurrent requests of each Nginx worker process as\nlong as there is *no* nonblocking I/O operations (including [ngx.sleep](#ngxsleep))\nin the middle of your calculations. As long as you do not give the\ncontrol back to the Nginx event loop and ngx_lua's light thread\nscheduler (even implicitly), there can never be any race conditions in\nbetween. For this reason, always be very careful when you want to share changeable data on the\nworker level. Buggy optimizations can easily lead to hard-to-debug\nrace conditions under load.\n\nIf server-wide data sharing is required, then use one or more of the following approaches:\n\n1. Use the [ngx.shared.DICT](#ngxshareddict) API provided by this module.\n1. Use only a single Nginx worker and a single server (this is however not recommended when there is a multi core CPU or multiple CPUs in a single machine).\n1. Use data storage mechanisms such as `memcached`, `redis`, `MySQL` or `PostgreSQL`. [The OpenResty official releases](https://openresty.org) come with a set of companion Nginx modules and Lua libraries that provide interfaces with these data storage mechanisms.\n\n[Back to TOC](#table-of-contents)\n\nKnown Issues\n============\n\n[Back to TOC](#table-of-contents)\n\nTCP socket connect operation issues\n-----------------------------------\n\nThe [tcpsock:connect](#tcpsockconnect) method may indicate `success` despite connection failures such as with `Connection Refused` errors.\n\nHowever, later attempts to manipulate the cosocket object will fail and return the actual error status message generated by the failed connect operation.\n\nThis issue is due to limitations in the Nginx event model and only appears to affect Mac OS X.\n\n[Back to TOC](#table-of-contents)\n\nLua Coroutine Yielding/Resuming\n-------------------------------\n\n* Because Lua's `dofile` and `require` builtins are currently implemented as C functions in LuaJIT 2.0/2.1, if the Lua file being loaded by `dofile` or `require` invokes [ngx.location.capture*](#ngxlocationcapture), [ngx.exec](#ngxexec), [ngx.exit](#ngxexit), or other API functions requiring yielding in the *top-level* scope of the Lua file, then the Lua error \"attempt to yield across C-call boundary\" will be raised. To avoid this, put these calls requiring yielding into your own Lua functions in the Lua file instead of the top-level scope of the file.\n\n[Back to TOC](#table-of-contents)\n\nLua Variable Scope\n------------------\n\nCare must be taken when importing modules, and this form should be used:\n\n```lua\n\n local xxx = require('xxx')\n```\n\ninstead of the old deprecated form:\n\n```lua\n\n require('xxx')\n```\n\nHere is the reason: by design, the global environment has exactly the same lifetime as the Nginx request handler associated with it. Each request handler has its own set of Lua global variables and that is the idea of request isolation. The Lua module is actually loaded by the first Nginx request handler and is cached by the `require()` built-in in the `package.loaded` table for later reference, and the `module()` builtin used by some Lua modules has the side effect of setting a global variable to the loaded module table. But this global variable will be cleared at the end of the request handler,  and every subsequent request handler all has its own (clean) global environment. So one will get Lua exception for accessing the `nil` value.\n\nThe use of Lua global variables is a generally inadvisable in the ngx_lua context as:\n\n1. the misuse of Lua globals has detrimental side effects on concurrent requests when such variables should instead be local in scope,\n1. Lua global variables require Lua table look-ups in the global environment which is computationally expensive, and\n1. some Lua global variable references may include typing errors which make such difficult to debug.\n\nIt is therefore *highly* recommended to always declare such within an appropriate local scope instead.\n\n```lua\n\n -- Avoid\n foo = 123\n -- Recommended\n local foo = 123\n\n -- Avoid\n function foo() return 123 end\n -- Recommended\n local function foo() return 123 end\n```\n\nTo find all instances of Lua global variables in your Lua code, run the [lua-releng tool](https://github.com/openresty/nginx-devel-utils/blob/master/lua-releng) across all `.lua` source files:\n\n    $ lua-releng\n    Checking use of Lua global variables in file lib/foo/bar.lua ...\n            1       [1489]  SETGLOBAL       7 -1    ; contains\n            55      [1506]  GETGLOBAL       7 -3    ; setvar\n            3       [1545]  GETGLOBAL       3 -4    ; varexpand\n\nThe output says that the line 1489 of file `lib/foo/bar.lua` writes to a global variable named `contains`, the line 1506 reads from the global variable `setvar`, and line 1545 reads the global `varexpand`.\n\nThis tool will guarantee that local variables in the Lua module functions are all declared with the `local` keyword, otherwise a runtime exception will be thrown. It prevents undesirable race conditions while accessing such variables. See [Data Sharing within an Nginx Worker](#data-sharing-within-an-nginx-worker) for the reasons behind this.\n\n[Back to TOC](#table-of-contents)\n\nLocations Configured by Subrequest Directives of Other Modules\n--------------------------------------------------------------\n\nThe [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi) directives cannot capture locations that include the [add_before_body](http://nginx.org/en/docs/http/ngx_http_addition_module.html#add_before_body), [add_after_body](http://nginx.org/en/docs/http/ngx_http_addition_module.html#add_after_body), [auth_request](https://nginx.org/en/docs/http/ngx_http_auth_request_module.html#auth_request), [echo_location](http://github.com/openresty/echo-nginx-module#echo_location), [echo_location_async](http://github.com/openresty/echo-nginx-module#echo_location_async), [echo_subrequest](http://github.com/openresty/echo-nginx-module#echo_subrequest), or [echo_subrequest_async](http://github.com/openresty/echo-nginx-module#echo_subrequest_async) directives.\n\n```nginx\n\n location /foo {\n     content_by_lua_block {\n         res = ngx.location.capture(\"/bar\")\n     }\n }\n location /bar {\n     echo_location /blah;\n }\n location /blah {\n     echo \"Success!\";\n }\n```\n\n```nginx\n\n $ curl -i http://example.com/foo\n```\n\nwill not work as expected.\n\n[Back to TOC](#table-of-contents)\n\nCosockets Not Available Everywhere\n----------------------------------\n\nDue to internal limitations in the Nginx core, the cosocket API is disabled in the following contexts: [set_by_lua*](#set_by_lua), [log_by_lua*](#log_by_lua), [header_filter_by_lua*](#header_filter_by_lua), and [body_filter_by_lua](#body_filter_by_lua).\n\nThe cosockets are currently also disabled in the [init_by_lua*](#init_by_lua) and [init_worker_by_lua*](#init_worker_by_lua) directive contexts but we may add support for these contexts in the future because there is no limitation in the Nginx core (or the limitation might be worked around).\n\nThere exists a workaround, however, when the original context does *not* need to wait for the cosocket results. That is, creating a zero-delay timer via the [ngx.timer.at](#ngxtimerat) API and do the cosocket results in the timer handler, which runs asynchronously as to the original context creating the timer.\n\n[Back to TOC](#table-of-contents)\n\nSpecial Escaping Sequences\n--------------------------\n\n**NOTE** Following the `v0.9.17` release, this pitfall can be avoided by using the `*_by_lua_block {}` configuration directives.\n\nPCRE sequences such as `\\d`, `\\s`, or `\\w`, require special attention because in string literals, the backslash character, `\\`, is stripped out by both the Lua language parser and by the Nginx config file parser before processing if not within a `*_by_lua_block {}` directive. So the following snippet will not work as expected:\n\n```nginx\n\n # nginx.conf\n ? location /test {\n ?     content_by_lua '\n ?         local regex = \"\\d+\"  -- THIS IS WRONG OUTSIDE OF A *_by_lua_block DIRECTIVE\n ?         local m = ngx.re.match(\"hello, 1234\", regex)\n ?         if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n ?     ';\n ? }\n # evaluates to \"not matched!\"\n```\n\nTo avoid this, *double* escape the backslash:\n\n```nginx\n\n # nginx.conf\n location /test {\n     content_by_lua '\n         local regex = \"\\\\\\\\d+\"\n         local m = ngx.re.match(\"hello, 1234\", regex)\n         if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n     ';\n }\n # evaluates to \"1234\"\n```\n\nHere, `\\\\\\\\d+` is stripped down to `\\\\d+` by the Nginx config file parser and this is further stripped down to `\\d+` by the Lua language parser before running.\n\nAlternatively, the regex pattern can be presented as a long-bracketed Lua string literal by encasing it in \"long brackets\", `[[...]]`, in which case backslashes have to only be escaped once for the Nginx config file parser.\n\n```nginx\n\n # nginx.conf\n location /test {\n     content_by_lua '\n         local regex = [[\\\\d+]]\n         local m = ngx.re.match(\"hello, 1234\", regex)\n         if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n     ';\n }\n # evaluates to \"1234\"\n```\n\nHere, `[[\\\\d+]]` is stripped down to `[[\\d+]]` by the Nginx config file parser and this is processed correctly.\n\nNote that a longer from of the long bracket, `[=[...]=]`, may be required if the regex pattern contains `[...]` sequences.\nThe `[=[...]=]` form may be used as the default form if desired.\n\n```nginx\n\n # nginx.conf\n location /test {\n     content_by_lua '\n         local regex = [=[[0-9]+]=]\n         local m = ngx.re.match(\"hello, 1234\", regex)\n         if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n     ';\n }\n # evaluates to \"1234\"\n```\n\nAn alternative approach to escaping PCRE sequences is to ensure that Lua code is placed in external script files and executed using the various `*_by_lua_file` directives.\nWith this approach, the backslashes are only stripped by the Lua language parser and therefore only need to be escaped once each.\n\n```lua\n\n -- test.lua\n local regex = \"\\\\d+\"\n local m = ngx.re.match(\"hello, 1234\", regex)\n if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n -- evaluates to \"1234\"\n```\n\nWithin external script files, PCRE sequences presented as long-bracketed Lua string literals do not require modification.\n\n```lua\n\n -- test.lua\n local regex = [[\\d+]]\n local m = ngx.re.match(\"hello, 1234\", regex)\n if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n -- evaluates to \"1234\"\n```\n\nAs noted earlier, PCRE sequences presented within `*_by_lua_block {}` directives (available following the `v0.9.17` release) do not require modification.\n\n```nginx\n\n # nginx.conf\n location /test {\n     content_by_lua_block {\n         local regex = [[\\d+]]\n         local m = ngx.re.match(\"hello, 1234\", regex)\n         if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n     }\n }\n # evaluates to \"1234\"\n```\n\n**NOTE** You are recommended to use `by_lua_file` when the Lua code is very long.\n\n[Back to TOC](#table-of-contents)\n\nMixing with SSI Not Supported\n-----------------------------\n\nMixing 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.\n\n[Back to TOC](#table-of-contents)\n\nSPDY Mode Not Fully Supported\n-----------------------------\n\nCertain Lua APIs provided by ngx_lua do not work in Nginx's SPDY mode yet: [ngx.location.capture](#ngxlocationcapture), [ngx.location.capture_multi](#ngxlocationcapture_multi), and [ngx.req.socket](#ngxreqsocket).\n\n[Back to TOC](#table-of-contents)\n\nMissing data on short circuited requests\n----------------------------------------\n\nNginx may terminate a request early with (at least):\n\n* 400 (Bad Request)\n* 405 (Not Allowed)\n* 408 (Request Timeout)\n* 413 (Request Entity Too Large)\n* 414 (Request URI Too Large)\n* 494 (Request Headers Too Large)\n* 499 (Client Closed Request)\n* 500 (Internal Server Error)\n* 501 (Not Implemented)\n\nThis means that phases that normally run are skipped, such as the rewrite or\naccess phase. This also means that later phases that are run regardless, e.g.\n[log_by_lua](#log_by_lua), will not have access to information that is normally set in those\nphases.\n\n[Back to TOC](#table-of-contents)\n\nTODO\n====\n\n* cosocket: implement LuaSocket's unconnected UDP API.\n* cosocket: add support in the context of [init_by_lua*](#init_by_lua).\n* cosocket: review and merge aviramc's [patch](https://github.com/openresty/lua-nginx-module/pull/290) for adding the `bsdrecv` method.\n* cosocket: add configure options for different strategies of handling the cosocket connection exceeding in the pools.\n* use `ngx_hash_t` to optimize the built-in header look-up process for [ngx.req.set_header](#ngxreqset_header), and etc.\n* 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.\n* add automatic Lua code time slicing support by yielding and resuming the Lua VM actively via Lua's debug hooks.\n* add `stat` mode similar to [mod_lua](https://httpd.apache.org/docs/trunk/mod/mod_lua.html).\n\n[Back to TOC](#table-of-contents)\n\nChanges\n=======\n\nThe changes made in every release of this module are listed in the change logs of the OpenResty bundle:\n\n<https://openresty.org/#Changes>\n\n[Back to TOC](#table-of-contents)\n\nBuild And Test\n==============\n\nThis module uses `.travis.yml` as the CI configuration.\nYou can always check `.travis.yml` for the latest CI configuration.\n\nFor developers, you need to run tests locally. You can use `util/run-ci.sh`\nto easily set up the environment and execute the test suite.\n\nTo run the Test from the beginning:\n\n```shell\ngit clone https://github.com/openresty/lua-nginx-module.git\ncd lua-nginx-module\nbash util/run-ci.sh\n```\n\nTest Suite\n==========\n\nThe following dependencies are required to run the test suite:\n\n* Nginx version >= 1.4.2\n\n* Perl modules:\n\t* Test::Nginx: <https://github.com/openresty/test-nginx>\n\n* Nginx modules:\n\t* [ngx_devel_kit](https://github.com/simplresty/ngx_devel_kit)\n\t* [ngx_set_misc](https://github.com/openresty/set-misc-nginx-module)\n\t* [ngx_auth_request](http://mdounin.ru/files/ngx_http_auth_request_module-0.2.tar.gz) (this is not needed if you're using Nginx 1.5.4+.\n\t* [ngx_echo](https://github.com/openresty/echo-nginx-module)\n\t* [ngx_memc](https://github.com/openresty/memc-nginx-module)\n\t* [ngx_srcache](https://github.com/openresty/srcache-nginx-module)\n\t* ngx_lua (i.e., this module)\n\t* [ngx_lua_upstream](https://github.com/openresty/lua-upstream-nginx-module)\n\t* [ngx_headers_more](https://github.com/openresty/headers-more-nginx-module)\n\t* [ngx_drizzle](https://github.com/openresty/drizzle-nginx-module)\n\t* [ngx_rds_json](https://github.com/openresty/rds-json-nginx-module)\n\t* [ngx_coolkit](https://github.com/FRiCKLE/ngx_coolkit)\n\t* [ngx_redis2](https://github.com/openresty/redis2-nginx-module)\n\nThe order in which these modules are added during configuration is important because the position of any filter module in the\nfiltering chain determines the final output, for example. The correct adding order is shown above.\n\n* 3rd-party Lua libraries:\n\t* [lua-cjson](https://www.kyne.au/~mark/software/lua-cjson.php)\n\n* Applications:\n\t* mysql: create database 'ngx_test', grant all privileges to user 'ngx_test', password is 'ngx_test'\n\t* memcached: listening on the default port, 11211.\n\t* redis: listening on the default port, 6379.\n\nSee also the [developer build script](https://github.com/openresty/lua-nginx-module/blob/master/util/build.sh) for more details on setting up the testing environment.\n\nTo run the whole test suite in the default testing mode:\n\n    cd /path/to/lua-nginx-module\n    export PATH=/path/to/your/nginx/sbin:$PATH\n    prove -I/path/to/test-nginx/lib -r t\n\nTo run specific test files:\n\n    cd /path/to/lua-nginx-module\n    export PATH=/path/to/your/nginx/sbin:$PATH\n    prove -I/path/to/test-nginx/lib t/002-content.t t/003-errors.t\n\n\nTo run a specific test block in a particular test file, add the line `--- ONLY` to the test block you want to run, and then use the `prove` utility to run that `.t` file.\n\nThere are also various testing modes based on mockeagain, valgrind, and etc. Refer to the [Test::Nginx documentation](https://search.cpan.org/perldoc?Test::Nginx) for more details for various advanced testing modes. See also the test reports for the Nginx test cluster running on Amazon EC2: <https://qa.openresty.org>.\n\n[Back to TOC](#table-of-contents)\n\nCopyright and License\n=====================\n\nThis module is licensed under the BSD license.\n\nCopyright (C) 2009-2017, by Xiaozhe Wang (chaoslawful) <chaoslawful@gmail.com>.\n\nCopyright (C) 2009-2025, by Yichun \"agentzh\" Zhang (章亦春) <agentzh@gmail.com>, OpenResty Inc.\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n[Back to TOC](#table-of-contents)\n\nSee Also\n========\n\nBlog posts:\n\n* [Introduction to Lua-Land CPU Flame Graphs](https://blog.openresty.com/en/lua-cpu-flame-graph/?src=gh_ngxlua)\n* [How OpenResty and Nginx Allocate and Manage Memory](https://blog.openresty.com/en//how-or-alloc-mem?src=gh_ngxlua)\n* [How OpenResty and Nginx Shared Memory Zones Consume RAM](https://blog.openresty.com/en/how-nginx-shm-consume-ram/?src=gh_ngxlua)\n* [Memory Fragmentation in OpenResty and Nginx's Shared Memory Zones](https://blog.openresty.com/en/nginx-shm-frag/?src=gh_ngxlua)\n\nOther related modules and libraries:\n\n* [ngx_stream_lua_module](https://github.com/openresty/stream-lua-nginx-module#readme) for an official port of this module for the Nginx \"stream\" subsystem (doing generic downstream TCP communications).\n* [lua-resty-memcached](https://github.com/openresty/lua-resty-memcached) library based on ngx_lua cosocket.\n* [lua-resty-redis](https://github.com/openresty/lua-resty-redis) library based on ngx_lua cosocket.\n* [lua-resty-mysql](https://github.com/openresty/lua-resty-mysql) library based on ngx_lua cosocket.\n* [lua-resty-upload](https://github.com/openresty/lua-resty-upload) library based on ngx_lua cosocket.\n* [lua-resty-dns](https://github.com/openresty/lua-resty-dns) library based on ngx_lua cosocket.\n* [lua-resty-websocket](https://github.com/openresty/lua-resty-websocket) library for both WebSocket server and client, based on ngx_lua cosocket.\n* [lua-resty-string](https://github.com/openresty/lua-resty-string) library based on [LuaJIT FFI](https://luajit.org/ext_ffi.html).\n* [lua-resty-lock](https://github.com/openresty/lua-resty-lock) library for a nonblocking simple lock API.\n* [lua-resty-cookie](https://github.com/cloudflare/lua-resty-cookie) library for HTTP cookie manipulation.\n* [Routing requests to different MySQL queries based on URI arguments](https://openresty.org/#RoutingMySQLQueriesBasedOnURIArgs)\n* [Dynamic Routing Based on Redis and Lua](https://openresty.org/#DynamicRoutingBasedOnRedis)\n* [Using LuaRocks with ngx_lua](https://openresty.org/#UsingLuaRocks)\n* [Introduction to ngx_lua](https://github.com/openresty/lua-nginx-module/wiki/Introduction)\n* [ngx_devel_kit](https://github.com/simplresty/ngx_devel_kit)\n* [echo-nginx-module](http://github.com/openresty/echo-nginx-module)\n* [drizzle-nginx-module](http://github.com/openresty/drizzle-nginx-module)\n* [postgres-nginx-module](https://github.com/FRiCKLE/ngx_postgres)\n* [memc-nginx-module](http://github.com/openresty/memc-nginx-module)\n* [The OpenResty bundle](https://openresty.org)\n* [Nginx Systemtap Toolkit](https://github.com/openresty/nginx-systemtap-toolkit)\n\n[Back to TOC](#table-of-contents)\n\nDirectives\n==========\n\n* [lua_load_resty_core](#lua_load_resty_core)\n* [lua_capture_error_log](#lua_capture_error_log)\n* [lua_use_default_type](#lua_use_default_type)\n* [lua_malloc_trim](#lua_malloc_trim)\n* [lua_code_cache](#lua_code_cache)\n* [lua_thread_cache_max_entries](#lua_thread_cache_max_entries)\n* [lua_regex_cache_max_entries](#lua_regex_cache_max_entries)\n* [lua_regex_match_limit](#lua_regex_match_limit)\n* [lua_package_path](#lua_package_path)\n* [lua_package_cpath](#lua_package_cpath)\n* [init_by_lua](#init_by_lua)\n* [init_by_lua_block](#init_by_lua_block)\n* [init_by_lua_file](#init_by_lua_file)\n* [init_worker_by_lua](#init_worker_by_lua)\n* [init_worker_by_lua_block](#init_worker_by_lua_block)\n* [init_worker_by_lua_file](#init_worker_by_lua_file)\n* [exit_worker_by_lua_block](#exit_worker_by_lua_block)\n* [exit_worker_by_lua_file](#exit_worker_by_lua_file)\n* [set_by_lua](#set_by_lua)\n* [set_by_lua_block](#set_by_lua_block)\n* [set_by_lua_file](#set_by_lua_file)\n* [precontent_by_lua_block](#precontent_by_lua_block)\n* [precontent_by_lua_file](#precontent_by_lua_file)\n* [content_by_lua](#content_by_lua)\n* [content_by_lua_block](#content_by_lua_block)\n* [content_by_lua_file](#content_by_lua_file)\n* [server_rewrite_by_lua_block](#server_rewrite_by_lua_block)\n* [server_rewrite_by_lua_file](#server_rewrite_by_lua_file)\n* [rewrite_by_lua](#rewrite_by_lua)\n* [rewrite_by_lua_block](#rewrite_by_lua_block)\n* [rewrite_by_lua_file](#rewrite_by_lua_file)\n* [access_by_lua](#access_by_lua)\n* [access_by_lua_block](#access_by_lua_block)\n* [access_by_lua_file](#access_by_lua_file)\n* [header_filter_by_lua](#header_filter_by_lua)\n* [header_filter_by_lua_block](#header_filter_by_lua_block)\n* [header_filter_by_lua_file](#header_filter_by_lua_file)\n* [body_filter_by_lua](#body_filter_by_lua)\n* [body_filter_by_lua_block](#body_filter_by_lua_block)\n* [body_filter_by_lua_file](#body_filter_by_lua_file)\n* [log_by_lua](#log_by_lua)\n* [log_by_lua_block](#log_by_lua_block)\n* [log_by_lua_file](#log_by_lua_file)\n* [balancer_by_lua_block](#balancer_by_lua_block)\n* [balancer_by_lua_file](#balancer_by_lua_file)\n* [balancer_keepalive](#balancer_keepalive)\n* [lua_need_request_body](#lua_need_request_body)\n* [ssl_client_hello_by_lua_block](#ssl_client_hello_by_lua_block)\n* [ssl_client_hello_by_lua_file](#ssl_client_hello_by_lua_file)\n* [ssl_certificate_by_lua_block](#ssl_certificate_by_lua_block)\n* [ssl_certificate_by_lua_file](#ssl_certificate_by_lua_file)\n* [ssl_session_fetch_by_lua_block](#ssl_session_fetch_by_lua_block)\n* [ssl_session_fetch_by_lua_file](#ssl_session_fetch_by_lua_file)\n* [ssl_session_store_by_lua_block](#ssl_session_store_by_lua_block)\n* [ssl_session_store_by_lua_file](#ssl_session_store_by_lua_file)\n* [proxy_ssl_certificate_by_lua_block](#proxy_ssl_certificate_by_lua_block)\n* [proxy_ssl_certificate_by_lua_file](#proxy_ssl_certificate_by_lua_file)\n* [proxy_ssl_verify_by_lua_block](#proxy_ssl_verify_by_lua_block)\n* [proxy_ssl_verify_by_lua_file](#proxy_ssl_verify_by_lua_file)\n* [lua_shared_dict](#lua_shared_dict)\n* [lua_socket_connect_timeout](#lua_socket_connect_timeout)\n* [lua_socket_send_timeout](#lua_socket_send_timeout)\n* [lua_socket_send_lowat](#lua_socket_send_lowat)\n* [lua_socket_read_timeout](#lua_socket_read_timeout)\n* [lua_socket_buffer_size](#lua_socket_buffer_size)\n* [lua_socket_pool_size](#lua_socket_pool_size)\n* [lua_socket_keepalive_timeout](#lua_socket_keepalive_timeout)\n* [lua_socket_log_errors](#lua_socket_log_errors)\n* [lua_ssl_ciphers](#lua_ssl_ciphers)\n* [lua_ssl_crl](#lua_ssl_crl)\n* [lua_ssl_protocols](#lua_ssl_protocols)\n* [lua_ssl_certificate](#lua_ssl_certificate)\n* [lua_ssl_certificate_key](#lua_ssl_certificate_key)\n* [lua_ssl_trusted_certificate](#lua_ssl_trusted_certificate)\n* [lua_ssl_verify_depth](#lua_ssl_verify_depth)\n* [lua_ssl_key_log](#lua_ssl_key_log)\n* [lua_ssl_conf_command](#lua_ssl_conf_command)\n* [lua_upstream_skip_openssl_default_verify](#lua_upstream_skip_openssl_default_verify)\n* [lua_http10_buffering](#lua_http10_buffering)\n* [rewrite_by_lua_no_postpone](#rewrite_by_lua_no_postpone)\n* [access_by_lua_no_postpone](#access_by_lua_no_postpone)\n* [precontent_by_lua_no_postpone](#precontent_by_lua_no_postpone)\n* [lua_transform_underscores_in_response_headers](#lua_transform_underscores_in_response_headers)\n* [lua_check_client_abort](#lua_check_client_abort)\n* [lua_max_pending_timers](#lua_max_pending_timers)\n* [lua_max_running_timers](#lua_max_running_timers)\n* [lua_sa_restart](#lua_sa_restart)\n* [lua_worker_thread_vm_pool_size](#lua_worker_thread_vm_pool_size)\n\n\nThe basic building blocks of scripting Nginx with Lua are directives. Directives are used to specify when the user Lua code is run and\nhow the result will be used. Below is a diagram showing the order in which directives are executed.\n\n![Lua Nginx Modules Directives](./doc/images/lua_nginx_modules_directives.drawio.png)\n\n[Back to TOC](#table-of-contents)\n\nlua_load_resty_core\n-------------------\n\n**syntax:** *lua_load_resty_core on|off*\n\n**default:** *lua_load_resty_core on*\n\n**context:** *http*\n\nThis directive is deprecated since the `v0.10.16` release of this\nmodule. The `resty.core` module from\n[lua-resty-core](https://github.com/openresty/lua-resty-core) is now mandatorily\nloaded during the Lua VM initialization. Specifying this directive will have no\neffect.\n\nThis directive was first introduced in the `v0.10.15` release and\nused to optionally load the `resty.core` module.\n\n[Back to TOC](#directives)\n\nlua_capture_error_log\n---------------------\n\n**syntax:** *lua_capture_error_log size*\n\n**default:** *none*\n\n**context:** *http*\n\nEnables a buffer of the specified `size` for capturing all the Nginx error log message data (not just those produced\nby this module or the Nginx http subsystem, but everything) without touching files or disks.\n\nYou can use units like `k` and `m` in the `size` value, as in\n\n```nginx\n\n lua_capture_error_log 100k;\n```\n\nAs a rule of thumb, a 4KB buffer can usually hold about 20 typical error log messages. So do the maths!\n\nThis buffer never grows. If it is full, new error log messages will replace the oldest ones in the buffer.\n\nThe size of the buffer must be bigger than the maximum length of a single error log message (which is 4K in OpenResty and 2K in stock NGINX).\n\nYou can read the messages in the buffer on the Lua land via the\n[get_logs()](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/errlog.md#get_logs)\nfunction of the\n[ngx.errlog](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/errlog.md#readme)\nmodule of the [lua-resty-core](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/errlog.md#readme)\nlibrary. This Lua API function will return the captured error log messages and\nalso remove these already read from the global capturing buffer, making room\nfor any new error log data. For this reason, the user should not configure this\nbuffer to be too big if the user read the buffered error log data fast enough.\n\nNote that the log level specified in the standard [error_log](https://nginx.org/r/error_log) directive\n*does* have effect on this capturing facility. It only captures log\nmessages of a level no lower than the specified log level in the [error_log](https://nginx.org/r/error_log) directive.\nThe user can still choose to set an even higher filtering log level on the fly via the Lua API function\n[errlog.set_filter_level](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/errlog.md#set_filter_level).\nSo it is more flexible than the static [error_log](https://nginx.org/r/error_log) directive.\n\nIt is worth noting that there is no way to capture the debugging logs\nwithout building OpenResty or Nginx with the `./configure`\noption `--with-debug`. And enabling debugging logs is\nstrongly discouraged in production builds due to high overhead.\n\nThis directive was first introduced in the `v0.10.9` release.\n\n[Back to TOC](#directives)\n\nlua_use_default_type\n--------------------\n\n**syntax:** *lua_use_default_type on | off*\n\n**default:** *lua_use_default_type on*\n\n**context:** *http, server, location, location if*\n\nSpecifies whether to use the MIME type specified by the [default_type](https://nginx.org/en/docs/http/ngx_http_core_module.html#default_type) directive for the default value of the `Content-Type` response header. Deactivate this directive if a default `Content-Type` response header for Lua request handlers is not desired.\n\nThis directive is turned on by default.\n\nThis directive was first introduced in the `v0.9.1` release.\n\n[Back to TOC](#directives)\n\nlua_malloc_trim\n---------------\n\n**syntax:** *lua_malloc_trim &lt;request-count&gt;*\n\n**default:** *lua_malloc_trim 1000*\n\n**context:** *http*\n\nAsks the underlying `libc` runtime library to release its cached free memory back to the operating system every\n`N` requests processed by the Nginx core. By default, `N` is 1000. You can configure the request count\nby using your own numbers. Smaller numbers mean more frequent releases, which may introduce higher CPU time consumption and\nsmaller memory footprint while larger numbers usually lead to less CPU time overhead and relatively larger memory footprint.\nJust tune the number for your own use cases.\n\nConfiguring the argument to `0` essentially turns off the periodical memory trimming altogether.\n\n```nginx\n\n lua_malloc_trim 0;  # turn off trimming completely\n```\n\nThe current implementation uses an Nginx log phase handler to do the request counting. So the appearance of the\n[log_subrequest on](https://nginx.org/en/docs/http/ngx_http_core_module.html#log_subrequest) directives in `nginx.conf`\nmay make the counting faster when subrequests are involved. By default, only \"main requests\" count.\n\nNote that this directive does *not* affect the memory allocated by LuaJIT's own allocator based on the `mmap`\nsystem call.\n\nThis directive was first introduced in the `v0.10.7` release.\n\n[Back to TOC](#directives)\n\nlua_code_cache\n--------------\n**syntax:** *lua_code_cache on | off*\n\n**default:** *lua_code_cache on*\n\n**context:** *http, server, location, location if*\n\nEnables or disables the Lua code cache for Lua code in `*_by_lua_file` directives (like [set_by_lua_file](#set_by_lua_file) and\n[content_by_lua_file](#content_by_lua_file)) and Lua modules.\n\nWhen turning off, every request served by ngx_lua will run in a separate Lua VM instance, starting from the `0.9.3` release. So the Lua files referenced in [set_by_lua_file](#set_by_lua_file),\n[content_by_lua_file](#content_by_lua_file), [access_by_lua_file](#access_by_lua_file),\nand etc will not be cached\nand all Lua modules used will be loaded from scratch. With this in place, developers can adopt an edit-and-refresh approach.\n\nPlease note however, that Lua code written inlined within nginx.conf\nsuch as those specified by [set_by_lua](#set_by_lua), [content_by_lua](#content_by_lua),\n[access_by_lua](#access_by_lua), and [rewrite_by_lua](#rewrite_by_lua) will not be updated when you edit the inlined Lua code in your `nginx.conf` file because only the Nginx config file parser can correctly parse the `nginx.conf`\nfile and the only way is to reload the config file\nby sending a `HUP` signal or just to restart Nginx.\n\nEven when the code cache is enabled, Lua files which are loaded by `dofile` or `loadfile`\nin *_by_lua_file cannot be cached (unless you cache the results yourself). Usually you can either use the [init_by_lua](#init_by_lua)\nor [init_by_lua_file](#init-by_lua_file) directives to load all such files or just make these Lua files true Lua modules\nand load them via `require`.\n\nThe ngx_lua module does not support the `stat` mode available with the\nApache `mod_lua` module (yet).\n\nDisabling the Lua code cache is strongly\ndiscouraged for production use and should only be used during\ndevelopment as it has a significant negative impact on overall performance. For example, the performance of a \"hello world\" Lua example can drop by an order of magnitude after disabling the Lua code cache.\n\n[Back to TOC](#directives)\n\nlua_thread_cache_max_entries\n----------------------------\n\n**syntax:** *lua_thread_cache_max_entries &lt;num&gt;*\n\n**default:** *lua_thread_cache_max_entries 1024*\n\n**context:** *http*\n\nSpecifies the maximum number of entries allowed in the worker process level lua thread object cache.\n\nThis cache recycles the lua thread GC objects among all our \"light threads\".\n\nA zero value of `<num>` disables the cache.\n\nNote that this feature requires OpenResty's LuaJIT with the new C API `lua_resetthread`.\n\nThis feature was first introduced in version `v0.10.9`.\n\n[Back to TOC](#directives)\n\nlua_regex_cache_max_entries\n---------------------------\n\n**syntax:** *lua_regex_cache_max_entries &lt;num&gt;*\n\n**default:** *lua_regex_cache_max_entries 1024*\n\n**context:** *http*\n\nSpecifies the maximum number of entries allowed in the worker process level compiled regex cache.\n\nThe regular expressions used in [ngx.re.match](#ngxrematch), [ngx.re.gmatch](#ngxregmatch), [ngx.re.sub](#ngxresub), and [ngx.re.gsub](#ngxregsub) will be cached within this cache if the regex option `o` (i.e., compile-once flag) is specified.\n\nThe default number of entries allowed is 1024 and when this limit is reached, new regular expressions will not be cached (as if the `o` option was not specified) and there will be one, and only one, warning in the `error.log` file:\n\n\n    2011/08/27 23:18:26 [warn] 31997#0: *1 lua exceeding regex cache max entries (1024), ...\n\n\nIf you are using the `ngx.re.*` implementation of [lua-resty-core](https://github.com/openresty/lua-resty-core) by loading the `resty.core.regex` module (or just the `resty.core` module), then an LRU cache is used for the regex cache being used here.\n\nDo not activate the `o` option for regular expressions (and/or `replace` string arguments for [ngx.re.sub](#ngxresub) and [ngx.re.gsub](#ngxregsub)) that are generated *on the fly* and give rise to infinite variations to avoid hitting the specified limit.\n\n[Back to TOC](#directives)\n\nlua_regex_match_limit\n---------------------\n\n**syntax:** *lua_regex_match_limit &lt;num&gt;*\n\n**default:** *lua_regex_match_limit 0*\n\n**context:** *http*\n\nSpecifies the \"match limit\" used by the PCRE library when executing the [ngx.re API](#ngxrematch). To quote the PCRE manpage, \"the limit ... has the effect of limiting the amount of backtracking that can take place.\"\n\nWhen the limit is hit, the error string \"pcre_exec() failed: -8\" will be returned by the [ngx.re API](#ngxrematch) functions on the Lua land.\n\nWhen setting the limit to 0, the default \"match limit\" when compiling the PCRE library is used. And this is the default value of this directive.\n\nThis directive was first introduced in the `v0.8.5` release.\n\n[Back to TOC](#directives)\n\nlua_package_path\n----------------\n\n**syntax:** *lua_package_path &lt;lua-style-path-str&gt;*\n\n**default:** *The content of LUA_PATH environment variable or Lua's compiled-in defaults.*\n\n**context:** *http*\n\nSets the Lua module search path used by scripts specified by [set_by_lua](#set_by_lua),\n[content_by_lua](#content_by_lua) and others. The path string is in standard Lua path form, and `;;`\ncan be used to stand for the original search paths.\n\nAs from the `v0.5.0rc29` release, the special notation `$prefix` or `${prefix}` can be used in the search path string to indicate the path of the `server prefix` usually determined by the `-p PATH` command-line option while starting the Nginx server.\n\n[Back to TOC](#directives)\n\nlua_package_cpath\n-----------------\n\n**syntax:** *lua_package_cpath &lt;lua-style-cpath-str&gt;*\n\n**default:** *The content of LUA_CPATH environment variable or Lua's compiled-in defaults.*\n\n**context:** *http*\n\nSets the Lua C-module search path used by scripts specified by [set_by_lua](#set_by_lua),\n[content_by_lua](#content_by_lua) and others. The cpath string is in standard Lua cpath form, and `;;`\ncan be used to stand for the original cpath.\n\nAs from the `v0.5.0rc29` release, the special notation `$prefix` or `${prefix}` can be used in the search path string to indicate the path of the `server prefix` usually determined by the `-p PATH` command-line option while starting the Nginx server.\n\n[Back to TOC](#directives)\n\ninit_by_lua\n-----------\n\n**syntax:** *init_by_lua &lt;lua-script-str&gt;*\n\n**context:** *http*\n\n**phase:** *loading-config*\n\n**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.\n\nSimilar to the [init_by_lua_block](#init_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires\nspecial character escaping).\n\nFor instance,\n\n```nginx\n\n init_by_lua '\n     print(\"I need no extra escaping here, for example: \\r\\nblah\")\n '\n```\n\nThis directive was first introduced in the `v0.5.5` release.\n\n[Back to TOC](#directives)\n\ninit_by_lua_block\n-----------------\n\n**syntax:** *init_by_lua_block { lua-script }*\n\n**context:** *http*\n\n**phase:** *loading-config*\n\n\nWhen 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.\n\nUsually 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:\n\n```nginx\n\n # this runs before forking out nginx worker processes:\n init_by_lua_block { require \"cjson\" }\n\n server {\n     location = /api {\n         content_by_lua_block {\n             -- the following require() will just  return\n             -- the already loaded module from package.loaded:\n             ngx.say(require \"cjson\".encode{dog = 5, cat = 6})\n         }\n     }\n }\n```\n\nYou can also initialize the [lua_shared_dict](#lua_shared_dict) shm storage at this phase. Here is an example for this:\n\n```nginx\n\n lua_shared_dict dogs 1m;\n\n init_by_lua_block {\n     local dogs = ngx.shared.dogs\n     dogs:set(\"Tom\", 56)\n }\n\n server {\n     location = /api {\n         content_by_lua_block {\n             local dogs = ngx.shared.dogs\n             ngx.say(dogs:get(\"Tom\"))\n         }\n     }\n }\n```\n\nBut 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.\n\nBecause 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.\n\nDo *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).\n\nOnly a small set of the [Nginx API for Lua](#nginx-api-for-lua) is supported in this context:\n\n* Logging APIs: [ngx.log](#ngxlog) and [print](#print),\n* Shared Dictionary API: [ngx.shared.DICT](#ngxshareddict).\n\nMore Nginx APIs for Lua may be supported in this context upon future user requests.\n\nBasically you can safely use Lua libraries that do blocking I/O in this very context because blocking the master process during server start-up is completely okay. Even the Nginx core does blocking I/O (at least on resolving upstream's host names) at the configure-loading phase.\n\nYou 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.\n\nThis directive was first introduced in the `v0.9.17` release.\n\nSee also the following blog posts for more details on OpenResty and Nginx's shared memory zones:\n\n* [How OpenResty and Nginx Shared Memory Zones Consume RAM](https://blog.openresty.com/en/how-nginx-shm-consume-ram/?src=gh_ngxlua)\n* [Memory Fragmentation in OpenResty and Nginx's Shared Memory Zones](https://blog.openresty.com/en/nginx-shm-frag/?src=gh_ngxlua)\n\n[Back to TOC](#directives)\n\ninit_by_lua_file\n----------------\n\n**syntax:** *init_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *http*\n\n**phase:** *loading-config*\n\nEquivalent to [init_by_lua_block](#init_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code or [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nWhen 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.\n\nThis directive was first introduced in the `v0.5.5` release.\n\n[Back to TOC](#directives)\n\ninit_worker_by_lua\n------------------\n\n**syntax:** *init_worker_by_lua &lt;lua-script-str&gt;*\n\n**context:** *http*\n\n**phase:** *starting-worker*\n\n**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.\n\nSimilar 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\nspecial character escaping).\n\nFor instance,\n\n```nginx\n\n init_worker_by_lua '\n     print(\"I need no extra escaping here, for example: \\r\\nblah\")\n ';\n```\n\nThis directive was first introduced in the `v0.9.5` release.\n\nThis hook no longer runs in the cache manager and cache loader processes since the `v0.10.12` release.\n\n[Back to TOC](#directives)\n\ninit_worker_by_lua_block\n------------------------\n\n**syntax:** *init_worker_by_lua_block { lua-script }*\n\n**context:** *http*\n\n**phase:** *starting-worker*\n\nRuns 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).\n\nThis 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,\n\n```nginx\n\n init_worker_by_lua_block {\n     local delay = 3  -- in seconds\n     local new_timer = ngx.timer.at\n     local log = ngx.log\n     local ERR = ngx.ERR\n     local check\n\n     check = function(premature)\n         if not premature then\n             -- do the health check or other routine work\n             local ok, err = new_timer(delay, check)\n             if not ok then\n                 log(ERR, \"failed to create timer: \", err)\n                 return\n             end\n         end\n\n         -- do something in timer\n     end\n\n     local hdl, err = new_timer(delay, check)\n     if not hdl then\n         log(ERR, \"failed to create timer: \", err)\n         return\n     end\n\n     -- other job in init_worker_by_lua\n }\n```\n\nThis directive was first introduced in the `v0.9.17` release.\n\nThis hook no longer runs in the cache manager and cache loader processes since the `v0.10.12` release.\n\n[Back to TOC](#directives)\n\ninit_worker_by_lua_file\n-----------------------\n\n**syntax:** *init_worker_by_lua_file &lt;lua-file-path&gt;*\n\n**context:** *http*\n\n**phase:** *starting-worker*\n\nSimilar 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.\n\nThis directive was first introduced in the `v0.9.5` release.\n\nThis hook no longer runs in the cache manager and cache loader processes since the `v0.10.12` release.\n\n[Back to TOC](#directives)\n\nexit_worker_by_lua_block\n------------------------\n\n**syntax:** *exit_worker_by_lua_block { lua-script }*\n\n**context:** *http*\n\n**phase:** *exiting-worker*\n\nRuns 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.\n\nThis 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.\n\nFor example,\n\n```nginx\n\n exit_worker_by_lua_block {\n     print(\"log from exit_worker_by_lua_block\")\n }\n```\n\nIt's not allowed to create a timer (even a 0-delay timer) here since it runs after all timers have been processed.\n\nThis directive was first introduced in the `v0.10.18` release.\n\n[Back to TOC](#directives)\n\nexit_worker_by_lua_file\n-----------------------\n\n**syntax:** *exit_worker_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *http*\n\n**phase:** *exiting-worker*\n\nSimilar to [exit_worker_by_lua_block](#exit_worker_by_lua_block), but accepts the file path to a Lua source file or Lua bytecode file.\n\nThis directive was first introduced in the `v0.10.18` release.\n\n[Back to TOC](#directives)\n\nset_by_lua\n----------\n\n**syntax:** *set_by_lua $res &lt;lua-script-str&gt; [$arg1 $arg2 ...]*\n\n**context:** *server, server if, location, location if*\n\n**phase:** *rewrite*\n\n**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.\n\nSimilar to the [set_by_lua_block](#set_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires\nspecial character escaping), and\n1. this directive support extra arguments after the Lua script.\n\nFor example,\n\n```nginx\n\n set_by_lua $res ' return 32 + math.cos(32) ';\n # $res now has the value \"32.834223360507\" or alike.\n```\n\nAs from the `v0.5.0rc29` release, Nginx variable interpolation is disabled in the `<lua-script-str>` argument of this directive and therefore, the dollar sign character (`$`) can be used directly.\n\nThis directive requires the [ngx_devel_kit](https://github.com/simplresty/ngx_devel_kit) module.\n\n[Back to TOC](#directives)\n\nset_by_lua_block\n----------------\n\n**syntax:** *set_by_lua_block $res { lua-script }*\n\n**context:** *server, server if, location, location if*\n\n**phase:** *rewrite*\n\nExecutes code specified inside a pair of curly braces (`{}`), and returns string output to `$res`.\nThe 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).\n\nThis 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.\n\nThis 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.\n\nAt least the following API functions are currently disabled within the context of `set_by_lua_block`:\n\n* Output API functions (e.g., [ngx.say](#ngxsay) and [ngx.send_headers](#ngxsend_headers))\n* Control API functions (e.g., [ngx.exit](#ngxexit))\n* Subrequest API functions (e.g., [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi))\n* Cosocket API functions (e.g., [ngx.socket.tcp](#ngxsockettcp) and [ngx.req.socket](#ngxreqsocket)).\n* Sleeping API function [ngx.sleep](#ngxsleep).\n\nIn addition, note that this directive can only write out a value to a single Nginx variable at\na time. However, a workaround is possible using the [ngx.var.VARIABLE](#ngxvarvariable) interface.\n\n```nginx\n\n location /foo {\n     set $diff ''; # we have to predefine the $diff variable here\n\n     set_by_lua_block $sum {\n         local a = 32\n         local b = 56\n\n         ngx.var.diff = a - b  -- write to $diff directly\n         return a + b          -- return the $sum value normally\n     }\n\n     echo \"sum = $sum, diff = $diff\";\n }\n```\n\nThis directive can be freely mixed with all directives of the [ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html), [set-misc-nginx-module](http://github.com/openresty/set-misc-nginx-module), and [array-var-nginx-module](http://github.com/openresty/array-var-nginx-module) modules. All of these directives will run in the same order as they appear in the config file.\n\n```nginx\n\n set $foo 32;\n set_by_lua_block $bar { return tonumber(ngx.var.foo) + 1 }\n set $baz \"bar: $bar\";  # $baz == \"bar: 33\"\n```\n\nNo special escaping is required in the Lua code block.\n\nThis directive requires the [ngx_devel_kit](https://github.com/simplresty/ngx_devel_kit) module.\n\nThis directive was first introduced in the `v0.9.17` release.\n\n[Back to TOC](#directives)\n\nset_by_lua_file\n---------------\n\n**syntax:** *set_by_lua_file $res &lt;path-to-lua-script-file&gt; [$arg1 $arg2 ...]*\n\n**context:** *server, server if, location, location if*\n\n**phase:** *rewrite*\n\nEquivalent to [set_by_lua_block](#set_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nNginx variable interpolation is supported in the `<path-to-lua-script-file>` argument string of this directive. But special care must be taken for injection attacks.\n\nWhen 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.\n\nWhen the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached\nand the Nginx config must be reloaded each time the Lua source file is modified.\nThe Lua code cache can be temporarily disabled during development by\nswitching [lua_code_cache](#lua_code_cache) `off` in `nginx.conf` to avoid reloading Nginx.\n\nThis directive requires the [ngx_devel_kit](https://github.com/simplresty/ngx_devel_kit) module.\n\n[Back to TOC](#directives)\n\nprecontent_by_lua_block\n--------------------\n\n**syntax:** *precontent_by_lua_block { lua-script }*\n\n**context:** *http, server, location, location if*\n\n**phase:** *precontent tail*\n\nActs as a precontent phase handler and executes Lua code string specified in `{ <lua-script }` for every request.\nThe 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).\n\nNote that this handler always runs *after* the standard [ngx_http_mirror_module](https://nginx.org/en/docs/http/ngx_http_mirror_module.html) and [ngx_http_try_files_module](https://nginx.org/en/docs/http/ngx_http_core_module.html#try_files). For example:\n\n```nginx\n location /images/ {\n     try_files $uri /images/default.gif;\n     precontent_by_lua_block {\n        ngx.log(ngx.NOTICE, \"file found\")\n     }\n }\n\n location = /images/default.gif {\n     expires 30s;\n     precontent_by_lua_block {\n        ngx.log(ngx.NOTICE, \"file not found, use default.gif instead\")\n     }\n }\n```\n\nThat is, if a request for /images/foo.jpg comes in and the file does not exist, the request will be internally redirected to /images/default.gif before [precontent_by_lua_block](#precontent_by_lua_block), and then the [precontent_by_lua_block](#precontent_by_lua_block) in new location will run and log \"file not found, use default.gif instead\".\n\nYou can use [precontent_by_lua_block](#precontent_by_lua_block) to perform some preparatory functions after the access phase handler but before the proxy or other content handler. Especially some functions that cannot be performed in [balancer_by_lua_block](#balancer_by_lua_block).\n\nyou can use the [precontent_by_lua_no_postpone](#precontent_by_lua_no_postpone) directive to control when to run this handler inside the \"precontent\" request-processing phase\nof Nginx.\n\n[Back to TOC](#directives)\n\nprecontent_by_lua_file\n-------------------\n\n**syntax:** *precontent_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *http, server, location, location if*\n\n**phase:** *precontent tail*\n\nEquivalent to [precontent_by_lua_block](#precontent_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nNginx variables can be used in the `<path-to-lua-script-file>` string to provide flexibility. This however carries some risks and is not ordinarily recommended.\n\nWhen 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.\n\nWhen the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached\nand the Nginx config must be reloaded each time the Lua source file is modified.\nThe Lua code cache can be temporarily disabled during development by switching [lua_code_cache](#lua_code_cache) `off` in `nginx.conf` to avoid repeatedly reloading Nginx.\n\nNginx variables are supported in the file path for dynamic dispatch just as in [content_by_lua_file](#content_by_lua_file).\n\nBut be very careful about malicious user inputs and always carefully validate or filter out the user-supplied path components.\n\n[Back to TOC](#directives)\n\ncontent_by_lua\n--------------\n\n**syntax:** *content_by_lua &lt;lua-script-str&gt;*\n\n**context:** *location, location if*\n\n**phase:** *content*\n\n**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.\n\nSimilar to the [content_by_lua_block](#content_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires\nspecial character escaping).\n\nFor instance,\n\n```nginx\n\n content_by_lua '\n     ngx.say(\"I need no extra escaping here, for example: \\r\\nblah\")\n ';\n```\n\n[Back to TOC](#directives)\n\ncontent_by_lua_block\n--------------------\n\n**syntax:** *content_by_lua_block { lua-script }*\n\n**context:** *location, location if*\n\n**phase:** *content*\n\nFor instance,\n\n```nginx\n\n content_by_lua_block {\n     ngx.say(\"I need no extra escaping here, for example: \\r\\nblah\")\n }\n```\n\nActs as a \"content handler\" and executes Lua code string specified in `{ lua-script }` for every request.\nThe 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).\n\nDo 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.\n\nThis directive was first introduced in the `v0.9.17` release.\n\n[Back to TOC](#directives)\n\ncontent_by_lua_file\n-------------------\n\n**syntax:** *content_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *location, location if*\n\n**phase:** *content*\n\nEquivalent to [content_by_lua_block](#content_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nIf 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.\n\nNginx variables can be used in the `<path-to-lua-script-file>` string to provide flexibility. This however carries some risks and is not ordinarily recommended.\n\nWhen 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.\n\nWhen the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached\nand the Nginx config must be reloaded each time the Lua source file is modified.\nThe Lua code cache can be temporarily disabled during development by\nswitching [lua_code_cache](#lua_code_cache) `off` in `nginx.conf` to avoid reloading Nginx.\n\nNginx variables are supported in the file path for dynamic dispatch, for example:\n\n```nginx\n\n # CAUTION: contents in nginx var must be carefully filtered,\n # otherwise there'll be great security risk!\n location ~ ^/app/([-_a-zA-Z0-9/]+) {\n     set $path $1;\n     content_by_lua_file /path/to/lua/app/root/$path.lua;\n }\n```\n\nBut be very careful about malicious user inputs and always carefully validate or filter out the user-supplied path components.\n\n[Back to TOC](#directives)\n\nserver_rewrite_by_lua_block\n---------------------------\n\n**syntax:** *server_rewrite_by_lua_block { lua-script }*\n\n**context:** *http, server*\n\n**phase:** *server rewrite*\n\nActs as a server rewrite phase handler and executes Lua code string specified in `{ lua-script }` for every request.\nThe 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).\n\n```nginx\n\n server {\n     ...\n\n     server_rewrite_by_lua_block {\n         ngx.ctx.a = \"server_rewrite_by_lua_block in http\"\n     }\n\n     location /lua {\n         content_by_lua_block {\n             ngx.say(ngx.ctx.a)\n             ngx.log(ngx.INFO, ngx.ctx.a)\n        \t}\n     }\n }\n```\n\nJust as any other rewrite phase handlers, [server_rewrite_by_lua_block](#server_rewrite_by_lua_block) also runs in subrequests.\n\n```nginx\n\n server {\n     server_rewrite_by_lua_block {\n         ngx.log(ngx.INFO, \"is_subrequest:\", ngx.is_subrequest)\n     }\n\n     location /lua {\n         content_by_lua_block {\n             local res = ngx.location.capture(\"/sub\")\n             ngx.print(res.body)\n         }\n     }\n\n     location /sub {\n         content_by_lua_block {\n             ngx.say(\"OK\")\n         }\n     }\n }\n```\n\nNote 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.\n\n\n```nginx\n\n server_rewrite_by_lua_block {\n     ngx.exit(503)\n }\n\n location /bar {\n     ...\n     # never exec\n }\n```\n\n\n[Back to TOC](#directives)\n\nserver_rewrite_by_lua_file\n--------------------------\n\n**syntax:** *server_rewrite_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *http, server*\n\n**phase:** *server rewrite*\n\nEquivalent to [server_rewrite_by_lua_block](#server_rewrite_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.10.22` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nNginx variables can be used in the `<path-to-lua-script-file>` string to provide flexibility. This however carries some risks and is not ordinarily recommended.\n\nWhen 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.\n\nWhen 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.\n\n[Back to TOC](#directives)\n\nrewrite_by_lua\n--------------\n\n**syntax:** *rewrite_by_lua &lt;lua-script-str&gt;*\n\n**context:** *http, server, location, location if*\n\n**phase:** *rewrite tail*\n\n**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.\n\nSimilar to the [rewrite_by_lua_block](#rewrite_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires\nspecial character escaping).\n\nFor instance,\n\n```nginx\n\n rewrite_by_lua '\n     do_something(\"hello, world!\\nhiya\\n\")\n ';\n```\n\n[Back to TOC](#directives)\n\nrewrite_by_lua_block\n--------------------\n\n**syntax:** *rewrite_by_lua_block { lua-script }*\n\n**context:** *http, server, location, location if*\n\n**phase:** *rewrite tail*\n\nActs as a rewrite phase handler and executes Lua code string specified in `{ lua-script }` for every request.\nThe 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).\n\nNote 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:\n\n```nginx\n\n location /foo {\n     set $a 12; # create and initialize $a\n     set $b \"\"; # create and initialize $b\n     rewrite_by_lua_block {\n         ngx.var.b = tonumber(ngx.var.a) + 1\n     }\n     echo \"res = $b\";\n }\n```\n\nbecause `set $a 12` and `set $b \"\"` run *before* [rewrite_by_lua_block](#rewrite_by_lua_block).\n\nOn the other hand, the following will not work as expected:\n\n```nginx\n\n ?  location /foo {\n ?      set $a 12; # create and initialize $a\n ?      set $b ''; # create and initialize $b\n ?      rewrite_by_lua_block {\n ?          ngx.var.b = tonumber(ngx.var.a) + 1\n ?      }\n ?      if ($b = '13') {\n ?         rewrite ^ /bar redirect;\n ?         break;\n ?      }\n ?\n ?      echo \"res = $b\";\n ?  }\n```\n\nbecause `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.\n\nThe right way of doing this is as follows:\n\n```nginx\n\n location /foo {\n     set $a 12; # create and initialize $a\n     set $b ''; # create and initialize $b\n     rewrite_by_lua_block {\n         ngx.var.b = tonumber(ngx.var.a) + 1\n         if tonumber(ngx.var.b) == 13 then\n             return ngx.redirect(\"/bar\")\n         end\n     }\n\n     echo \"res = $b\";\n }\n```\n\nNote 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,\n\n```nginx\n\n location / {\n     eval $res {\n         proxy_pass http://foo.com/check-spam;\n     }\n\n     if ($res = 'spam') {\n         rewrite ^ /terms-of-use.html redirect;\n     }\n\n     fastcgi_pass ...;\n }\n```\n\ncan be implemented in ngx_lua as:\n\n```nginx\n\n location = /check-spam {\n     internal;\n     proxy_pass http://foo.com/check-spam;\n }\n\n location / {\n     rewrite_by_lua_block {\n         local res = ngx.location.capture(\"/check-spam\")\n         if res.body == \"spam\" then\n             return ngx.redirect(\"/terms-of-use.html\")\n         end\n     }\n\n     fastcgi_pass ...;\n }\n```\n\nJust as any other rewrite phase handlers, [rewrite_by_lua_block](#rewrite_by_lua_block) also runs in subrequests.\n\nNote 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.\n\nIf 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,\n\n```nginx\n\n location /foo {\n     rewrite ^ /bar;\n     rewrite_by_lua_block {\n         ngx.exit(503)\n     }\n }\n location /bar {\n     ...\n }\n```\n\nHere 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.\n\nThe `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.\n\nThis directive was first introduced in the `v0.9.17` release.\n\n[Back to TOC](#directives)\n\nrewrite_by_lua_file\n-------------------\n\n**syntax:** *rewrite_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *http, server, location, location if*\n\n**phase:** *rewrite tail*\n\nEquivalent to [rewrite_by_lua_block](#rewrite_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nNginx variables can be used in the `<path-to-lua-script-file>` string to provide flexibility. This however carries some risks and is not ordinarily recommended.\n\nWhen 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.\n\nWhen 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.\n\nThe `rewrite_by_lua_file` 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.\n\nNginx variables are supported in the file path for dynamic dispatch just as in [content_by_lua_file](#content_by_lua_file).\n\n[Back to TOC](#directives)\n\naccess_by_lua\n-------------\n\n**syntax:** *access_by_lua &lt;lua-script-str&gt;*\n\n**context:** *http, server, location, location if*\n\n**phase:** *access tail*\n\n**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.\n\nSimilar to the [access_by_lua_block](#access_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires\nspecial character escaping).\n\nFor instance,\n\n```nginx\n\n access_by_lua '\n     do_something(\"hello, world!\\nhiya\\n\")\n ';\n```\n\n[Back to TOC](#directives)\n\naccess_by_lua_block\n-------------------\n\n**syntax:** *access_by_lua_block { lua-script }*\n\n**context:** *http, server, location, location if*\n\n**phase:** *access tail*\n\nActs as an access phase handler and executes Lua code string specified in `{ <lua-script }` for every request.\nThe 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).\n\nNote that this handler always runs *after* the standard [ngx_http_access_module](http://nginx.org/en/docs/http/ngx_http_access_module.html). So the following will work as expected:\n\n```nginx\n\n location / {\n     deny    192.168.1.1;\n     allow   192.168.1.0/24;\n     allow   10.1.1.0/16;\n     deny    all;\n\n     access_by_lua_block {\n         local res = ngx.location.capture(\"/mysql\", { ... })\n         ...\n     }\n\n     # proxy_pass/fastcgi_pass/...\n }\n```\n\nThat 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).\n\nNote that the [ngx_auth_request](http://mdounin.ru/hg/ngx_http_auth_request_module/) module can be approximated by using [access_by_lua_block](#access_by_lua_block):\n\n```nginx\n\n location / {\n     auth_request /auth;\n\n     # proxy_pass/fastcgi_pass/postgres_pass/...\n }\n```\n\ncan be implemented in ngx_lua as:\n\n```nginx\n\n location / {\n     access_by_lua_block {\n         local res = ngx.location.capture(\"/auth\")\n\n         if res.status == ngx.HTTP_OK then\n             return\n         end\n\n         if res.status == ngx.HTTP_FORBIDDEN then\n             ngx.exit(res.status)\n         end\n\n         ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\n     }\n\n     # proxy_pass/fastcgi_pass/postgres_pass/...\n }\n```\n\nAs with other access phase handlers, [access_by_lua_block](#access_by_lua_block) will *not* run in subrequests.\n\nNote 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.\n\nStarting from the `v0.9.20` release, you can use the [access_by_lua_no_postpone](#access_by_lua_no_postpone)\ndirective to control when to run this handler inside the \"access\" request-processing phase\nof Nginx.\n\nThis directive was first introduced in the `v0.9.17` release.\n\n[Back to TOC](#directives)\n\naccess_by_lua_file\n------------------\n\n**syntax:** *access_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *http, server, location, location if*\n\n**phase:** *access tail*\n\nEquivalent to [access_by_lua_block](#access_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nNginx variables can be used in the `<path-to-lua-script-file>` string to provide flexibility. This however carries some risks and is not ordinarily recommended.\n\nWhen 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.\n\nWhen the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached\nand the Nginx config must be reloaded each time the Lua source file is modified.\nThe Lua code cache can be temporarily disabled during development by switching [lua_code_cache](#lua_code_cache) `off` in `nginx.conf` to avoid repeatedly reloading Nginx.\n\nNginx variables are supported in the file path for dynamic dispatch just as in [content_by_lua_file](#content_by_lua_file).\n\n[Back to TOC](#directives)\n\nheader_filter_by_lua\n--------------------\n\n**syntax:** *header_filter_by_lua &lt;lua-script-str&gt;*\n\n**context:** *http, server, location, location if*\n\n**phase:** *output-header-filter*\n\n**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.\n\nSimilar 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\nspecial character escaping).\n\nFor instance,\n\n```nginx\n\n header_filter_by_lua '\n     ngx.header[\"content-length\"] = nil\n ';\n```\n\nThis directive was first introduced in the `v0.2.1rc20` release.\n\n[Back to TOC](#directives)\n\nheader_filter_by_lua_block\n--------------------------\n\n**syntax:** *header_filter_by_lua_block { lua-script }*\n\n**context:** *http, server, location, location if*\n\n**phase:** *output-header-filter*\n\nUses Lua code specified in `{ lua-script }` to define an output header filter.\n\nNote that the following API functions are currently disabled within this context:\n\n* Output API functions (e.g., [ngx.say](#ngxsay) and [ngx.send_headers](#ngxsend_headers))\n* Control API functions (e.g., [ngx.redirect](#ngxredirect) and [ngx.exec](#ngxexec))\n* Subrequest API functions (e.g., [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi))\n* Cosocket API functions (e.g., [ngx.socket.tcp](#ngxsockettcp) and [ngx.req.socket](#ngxreqsocket)).\n\nHere is an example of overriding a response header (or adding one if absent) in our Lua header filter:\n\n```nginx\n\n location / {\n     proxy_pass http://mybackend;\n     header_filter_by_lua_block {\n         ngx.header.Foo = \"blah\"\n     }\n }\n```\n\nThis directive was first introduced in the `v0.9.17` release.\n\n[Back to TOC](#directives)\n\nheader_filter_by_lua_file\n-------------------------\n\n**syntax:** *header_filter_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *http, server, location, location if*\n\n**phase:** *output-header-filter*\n\nEquivalent to [header_filter_by_lua_block](#header_filter_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nWhen 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.\n\nThis directive was first introduced in the `v0.2.1rc20` release.\n\n[Back to TOC](#directives)\n\nbody_filter_by_lua\n------------------\n\n**syntax:** *body_filter_by_lua &lt;lua-script-str&gt;*\n\n**context:** *http, server, location, location if*\n\n**phase:** *output-body-filter*\n\n**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.\n\nSimilar 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\nspecial character escaping).\n\nFor instance,\n\n```nginx\n\n body_filter_by_lua '\n     local data, eof = ngx.arg[1], ngx.arg[2]\n ';\n```\n\nThis directive was first introduced in the `v0.5.0rc32` release.\n\n[Back to TOC](#directives)\n\nbody_filter_by_lua_block\n------------------------\n\n**syntax:** *body_filter_by_lua_block { lua-script-str }*\n\n**context:** *http, server, location, location if*\n\n**phase:** *output-body-filter*\n\nUses Lua code specified in `{ lua-script }` to define an output body filter.\n\nThe 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).\n\nBehind the scene, the \"eof\" flag is just the `last_buf` (for main requests) or `last_in_chain` (for subrequests) flag of the Nginx chain link buffers. (Before the `v0.7.14` release, the \"eof\" flag does not work at all in subrequests.)\n\nThe output data stream can be aborted immediately by running the following Lua statement:\n\n```lua\n\n return ngx.ERROR\n```\n\nThis will truncate the response body and usually result in incomplete and also invalid responses.\n\nThe Lua code can pass its own modified version of the input data chunk to the downstream Nginx output body filters by overriding [ngx.arg](#ngxarg)\\[1\\] with a Lua string or a Lua table of strings. For example, to transform all the lowercase letters in the response body, we can just write:\n\n```nginx\n\n location / {\n     proxy_pass http://mybackend;\n     body_filter_by_lua_block {\n         ngx.arg[1] = string.upper(ngx.arg[1])\n     }\n }\n```\n\nWhen setting `nil` or an empty Lua string value to `ngx.arg[1]`, no data chunk will be passed to the downstream Nginx output filters at all.\n\nLikewise, new \"eof\" flag can also be specified by setting a boolean value to [ngx.arg](#ngxarg)\\[2\\]. For example,\n\n```nginx\n\n location /t {\n     echo hello world;\n     echo hiya globe;\n\n     body_filter_by_lua_block {\n         local chunk = ngx.arg[1]\n         if string.match(chunk, \"hello\") then\n             ngx.arg[2] = true  -- new eof\n             return\n         end\n\n         -- just throw away any remaining chunk data\n         ngx.arg[1] = nil\n     }\n }\n```\n\nThen `GET /t` will just return the output\n\n\n    hello world\n\n\nThat is, when the body filter sees a chunk containing the word \"hello\", then it will set the \"eof\" flag to true immediately, resulting in truncated but still valid responses.\n\nWhen the Lua code may change the length of the response body, then it is required to always clear out the `Content-Length` response header (if any) in a header filter to enforce streaming output, as in\n\n```nginx\n\n location /foo {\n     # fastcgi_pass/proxy_pass/...\n\n     header_filter_by_lua_block {\n         ngx.header.content_length = nil\n     }\n     body_filter_by_lua_block {\n         ngx.arg[1] = string.len(ngx.arg[1]) .. \"\\n\"\n     }\n }\n```\n\nNote that the following API functions are currently disabled within this context due to the limitations in Nginx output filter's current implementation:\n\n* Output API functions (e.g., [ngx.say](#ngxsay) and [ngx.send_headers](#ngxsend_headers))\n* Control API functions (e.g., [ngx.exit](#ngxexit) and [ngx.exec](#ngxexec))\n* Subrequest API functions (e.g., [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi))\n* Cosocket API functions (e.g., [ngx.socket.tcp](#ngxsockettcp) and [ngx.req.socket](#ngxreqsocket)).\n\nNginx 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.\n\nThis directive was first introduced in the `v0.9.17` release.\n\n[Back to TOC](#directives)\n\nbody_filter_by_lua_file\n-----------------------\n\n**syntax:** *body_filter_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *http, server, location, location if*\n\n**phase:** *output-body-filter*\n\nEquivalent to [body_filter_by_lua_block](#body_filter_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nWhen 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.\n\nThis directive was first introduced in the `v0.5.0rc32` release.\n\n[Back to TOC](#directives)\n\nlog_by_lua\n----------\n\n**syntax:** *log_by_lua &lt;lua-script-str&gt;*\n\n**context:** *http, server, location, location if*\n\n**phase:** *log*\n\n**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.\n\nSimilar to the [log_by_lua_block](#log_by_lua_block) directive, but accepts the Lua source directly in an Nginx string literal (which requires\nspecial character escaping).\n\nFor instance,\n\n```nginx\n\n log_by_lua '\n     print(\"I need no extra escaping here, for example: \\r\\nblah\")\n ';\n```\n\nThis directive was first introduced in the `v0.5.0rc31` release.\n\n[Back to TOC](#directives)\n\nlog_by_lua_block\n----------------\n\n**syntax:** *log_by_lua_block { lua-script }*\n\n**context:** *http, server, location, location if*\n\n**phase:** *log*\n\nRuns 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.\n\nNote that the following API functions are currently disabled within this context:\n\n* Output API functions (e.g., [ngx.say](#ngxsay) and [ngx.send_headers](#ngxsend_headers))\n* Control API functions (e.g., [ngx.exit](#ngxexit))\n* Subrequest API functions (e.g., [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi))\n* Cosocket API functions (e.g., [ngx.socket.tcp](#ngxsockettcp) and [ngx.req.socket](#ngxreqsocket)).\n\nHere is an example of gathering average data for [$upstream_response_time](http://nginx.org/en/docs/http/ngx_http_upstream_module.html#var_upstream_response_time):\n\n```nginx\n\n lua_shared_dict log_dict 5M;\n\n server {\n     location / {\n         proxy_pass http://mybackend;\n\n         log_by_lua_block {\n             local log_dict = ngx.shared.log_dict\n             local upstream_time = tonumber(ngx.var.upstream_response_time)\n\n             local sum = log_dict:get(\"upstream_time-sum\") or 0\n             sum = sum + upstream_time\n             log_dict:set(\"upstream_time-sum\", sum)\n\n             local newval, err = log_dict:incr(\"upstream_time-nb\", 1)\n             if not newval and err == \"not found\" then\n                 log_dict:add(\"upstream_time-nb\", 0)\n                 log_dict:incr(\"upstream_time-nb\", 1)\n             end\n         }\n     }\n\n     location = /status {\n         content_by_lua_block {\n             local log_dict = ngx.shared.log_dict\n             local sum = log_dict:get(\"upstream_time-sum\")\n             local nb = log_dict:get(\"upstream_time-nb\")\n\n             if nb and sum then\n                 ngx.say(\"average upstream response time: \", sum / nb,\n                         \" (\", nb, \" reqs)\")\n             else\n                 ngx.say(\"no data yet\")\n             end\n         }\n     }\n }\n```\n\nThis directive was first introduced in the `v0.9.17` release.\n\n[Back to TOC](#directives)\n\nlog_by_lua_file\n---------------\n\n**syntax:** *log_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *http, server, location, location if*\n\n**phase:** *log*\n\nEquivalent to [log_by_lua_block](#log_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nWhen 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.\n\nThis directive was first introduced in the `v0.5.0rc31` release.\n\n[Back to TOC](#directives)\n\nbalancer_by_lua_block\n---------------------\n\n**syntax:** *balancer_by_lua_block { lua-script }*\n\n**context:** *upstream*\n\n**phase:** *content*\n\nThis directive runs Lua code as an upstream balancer for any upstream entities defined\nby the `upstream {}` configuration block.\n\nFor instance,\n\n```nginx\n\n upstream foo {\n     server 127.0.0.1;\n     balancer_by_lua_block {\n         -- use Lua to do something interesting here\n         -- as a dynamic balancer\n     }\n }\n\n server {\n     location / {\n         proxy_pass http://foo;\n     }\n }\n```\n\nThe resulting Lua load balancer can work with any existing Nginx upstream modules\nlike [ngx_proxy](https://nginx.org/en/docs/http/ngx_http_proxy_module.html) and\n[ngx_fastcgi](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html).\n\nAlso, the Lua load balancer can work with the standard upstream connection pool mechanism,\ni.e., the standard [keepalive](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive) directive.\nJust ensure that the [keepalive](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive) directive\nis used *after* this `balancer_by_lua_block` directive in a single `upstream {}` configuration block.\n\nThe Lua load balancer can totally ignore the list of servers defined in the `upstream {}` block\nand select peer from a completely dynamic server list (even changing per request) via the\n[ngx.balancer](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/balancer.md) module\nfrom the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.\n\nThe Lua code handler registered by this directive might get called more than once in a single\ndownstream request when the Nginx upstream mechanism retries the request on conditions\nspecified by directives like the [proxy_next_upstream](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream)\ndirective.\n\nThis Lua code execution context does not support yielding, so Lua APIs that may yield\n(like cosockets and \"light threads\") are disabled in this context. One can usually work\naround this limitation by doing such operations in an earlier phase handler (like\n[precontent_by_lua*](#precontent_by_lua_block)) and passing along the result into this context\nvia the [ngx.ctx](#ngxctx) table.\n\nThis directive was first introduced in the `v0.10.0` release.\n\n[Back to TOC](#directives)\n\nbalancer_by_lua_file\n--------------------\n\n**syntax:** *balancer_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *upstream*\n\n**phase:** *content*\n\nEquivalent to [balancer_by_lua_block](#balancer_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nWhen 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.\n\nThis directive was first introduced in the `v0.10.0` release.\n\n[Back to TOC](#directives)\n\nbalancer_keepalive\n------------------\n\n**syntax:** *balancer_keepalive &lt;total-connections&gt;*\n\n**context:** *upstream*\n\n**phase:** *loading-config*\n\nThe `total-connections` parameter sets the maximum number of idle\nkeepalive connections to upstream servers that are preserved in the cache of\neach worker process. When this number is exceeded, the least recently used\nconnections are closed.\n\nIt should be particularly noted that the keepalive directive does not limit the\ntotal number of connections to upstream servers that an nginx worker process\ncan open. The connections parameter should be set to a number small enough to\nlet upstream servers process new incoming connections as well.\n\nThis directive was first introduced in the `v0.10.21` release.\n\n[Back to TOC](#directives)\n\nlua_need_request_body\n---------------------\n\n**syntax:** *lua_need_request_body &lt;on|off&gt;*\n\n**default:** *off*\n\n**context:** *http, server, location, location if*\n\n**phase:** *depends on usage*\n\nDetermines whether to force the request body data to be read before running rewrite/access/content_by_lua* or not. The Nginx core does not read the client request body by default and if request body data is required, then this directive should be turned `on` or the [ngx.req.read_body](#ngxreqread_body) function should be called within the Lua code.\n\nTo read the request body data within the [$request_body](http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_body) variable,\n[client_body_buffer_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size) must have the same value as [client_max_body_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size). Because when the content length exceeds [client_body_buffer_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size) but less than [client_max_body_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size), Nginx will buffer the data into a temporary file on the disk, which will lead to empty value in the [$request_body](http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_body) variable.\n\nIf the current location includes [rewrite_by_lua*](#rewrite_by_lua) directives,\nthen the request body will be read just before the [rewrite_by_lua*](#rewrite_by_lua) code is run (and also at the\n`rewrite` phase). Similarly, if only [content_by_lua](#content_by_lua) is specified,\nthe request body will not be read until the content handler's Lua code is\nabout to run (i.e., the request body will be read during the content phase).\n\nIt is recommended however, to use the [ngx.req.read_body](#ngxreqread_body) and [ngx.req.discard_body](#ngxreqdiscard_body) functions for finer control over the request body reading process instead.\n\nThis also applies to [access_by_lua*](#access_by_lua).\n\n[Back to TOC](#directives)\n\nssl_client_hello_by_lua_block\n-----------------------------\n\n**syntax:** *ssl_client_hello_by_lua_block { lua-script }*\n\n**context:** *http, server*\n\n**phase:** *right-after-client-hello-message-was-processed*\n\nThis directive runs user Lua code when Nginx is about to post-process the SSL client hello message for the downstream\nSSL (https) connections.\n\nIt is particularly useful for dynamically setting the SSL protocols according to the SNI.\n\nIt is also useful to do some custom operations according to the per-connection information in the client hello message.\n\nFor example, one can parse custom client hello extension and do the corresponding handling in pure Lua.\n\nThis Lua handler will always run whether the SSL session is resumed (via SSL session IDs or TLS session tickets) or not.\nWhile the `ssl_certificate_by_lua*` Lua handler will only runs when initiating a full SSL handshake.\n\nThe [ngx.ssl.clienthello](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md) Lua modules\nprovided by the [lua-resty-core](https://github.com/openresty/lua-resty-core/#readme)\nlibrary are particularly useful in this context.\n\nNote that this handler runs in extremely early stage of SSL handshake, before the SSL client hello extensions are parsed.\nSo you can not use some Lua API like `ssl.server_name()` which is dependent on the later stage's processing.\n\nAlso note that only the directive in default server is valid for several virtual servers with the same IP address and port.\n\nBelow is a trivial example using the\n[ngx.ssl.clienthello](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md) module\nat the same time:\n\n```nginx\n\n server {\n     listen 443 ssl;\n     server_name   test.com;\n     ssl_certificate /path/to/cert.crt;\n     ssl_certificate_key /path/to/key.key;\n     ssl_client_hello_by_lua_block {\n         local ssl_clt = require \"ngx.ssl.clienthello\"\n         local host, err = ssl_clt.get_client_hello_server_name()\n         if host == \"test.com\" then\n             ssl_clt.set_protocols({\"TLSv1\", \"TLSv1.1\"})\n         elseif host == \"test2.com\" then\n             ssl_clt.set_protocols({\"TLSv1.2\", \"TLSv1.3\"})\n         elseif not host then\n             ngx.log(ngx.ERR, \"failed to get the SNI name: \", err)\n             ngx.exit(ngx.ERROR)\n         else\n             ngx.log(ngx.ERR, \"unknown SNI name: \", host)\n             ngx.exit(ngx.ERROR)\n         end\n     }\n     ...\n }\n server {\n     listen 443 ssl;\n     server_name   test2.com;\n     ssl_certificate /path/to/cert.crt;\n     ssl_certificate_key /path/to/key.key;\n     ...\n }\n```\n\nSee more information in the [ngx.ssl.clienthello](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md)\nLua modules' official documentation.\n\nUncaught Lua exceptions in the user Lua code immediately abort the current SSL session, so does the\n[ngx.exit](#ngxexit) call with an error code like `ngx.ERROR`.\n\nThis Lua code execution context *does* support yielding, so Lua APIs that may yield\n(like cosockets, sleeping, and \"light threads\")\nare enabled in this context\n\nNote, you need to configure the [ssl_certificate](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate)\nand [ssl_certificate_key](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate_key)\nto avoid the following error while starting NGINX:\n\n\n    nginx: [emerg] no ssl configured for the server\n\n\nThis directive requires OpenSSL 1.1.1 or greater.\n\nIf you are using the [official pre-built\npackages](https://openresty.org/en/linux-packages.html) for\n[OpenResty](https://openresty.org/) 1.21.4.1 or later, then everything should\nwork out of the box.\n\nIf you are not using the Nginx core shipped with\n[OpenResty](https://openresty.org) 1.21.4.1 or later, you will need to apply\npatches to the standard Nginx core:\n\n<https://openresty.org/en/nginx-ssl-patches.html>\n\n**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)\n\nThis directive was first introduced in the `v0.10.21` release.\n\n[Back to TOC](#directives)\n\nssl_client_hello_by_lua_file\n----------------------------\n\n**syntax:** *ssl_client_hello_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *http, server*\n\n**phase:** *right-after-client-hello-message-was-processed*\n\nEquivalent to [ssl_client_hello_by_lua_block](#ssl_client_hello_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nWhen 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.\n\n**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)\n\nThis directive was first introduced in the `v0.10.21` release.\n\n[Back to TOC](#directives)\n\nssl_certificate_by_lua_block\n----------------------------\n\n**syntax:** *ssl_certificate_by_lua_block { lua-script }*\n\n**context:** *server*\n\n**phase:** *right-before-SSL-handshake*\n\nThis directive runs user Lua code when Nginx is about to start the SSL handshake for the downstream\nSSL (https) connections.\n\nIt is particularly useful for setting the SSL certificate chain and the corresponding private key on a per-request\nbasis. It is also useful to load such handshake configurations nonblockingly from the remote (for example,\nwith the [cosocket](#ngxsockettcp) API). And one can also do per-request OCSP stapling handling in pure\nLua here as well.\n\nAnother typical use case is to do SSL handshake traffic control nonblockingly in this context,\nwith the help of the [lua-resty-limit-traffic#readme](https://github.com/openresty/lua-resty-limit-traffic)\nlibrary, for example.\n\nOne can also do interesting things with the SSL handshake requests from the client side, like\nrejecting old SSL clients using the SSLv3 protocol or even below selectively.\n\nThe [ngx.ssl](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md)\nand [ngx.ocsp](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ocsp.md) Lua modules\nprovided by the [lua-resty-core](https://github.com/openresty/lua-resty-core/#readme)\nlibrary are particularly useful in this context. You can use the Lua API offered by these two Lua modules\nto manipulate the SSL certificate chain and private key for the current SSL connection\nbeing initiated.\n\nThis Lua handler does not run at all, however, when Nginx/OpenSSL successfully resumes\nthe SSL session via SSL session IDs or TLS session tickets for the current SSL connection. In\nother words, this Lua handler only runs when Nginx has to initiate a full SSL handshake.\n\nBelow is a trivial example using the\n[ngx.ssl](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md) module\nat the same time:\n\n```nginx\n\n server {\n     listen 443 ssl;\n     server_name   test.com;\n\n     ssl_certificate_by_lua_block {\n         print(\"About to initiate a new SSL handshake!\")\n     }\n\n     location / {\n         root html;\n     }\n }\n```\n\nSee more complicated examples in the [ngx.ssl](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md)\nand [ngx.ocsp](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ocsp.md)\nLua modules' official documentation.\n\nUncaught Lua exceptions in the user Lua code immediately abort the current SSL session, so does the\n[ngx.exit](#ngxexit) call with an error code like `ngx.ERROR`.\n\nThis Lua code execution context *does* support yielding, so Lua APIs that may yield\n(like cosockets, sleeping, and \"light threads\")\nare enabled in this context.\n\nNote, however, you still need to configure the [ssl_certificate](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate) and\n[ssl_certificate_key](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate_key)\ndirectives even though you will not use this static certificate and private key at all. This is\nbecause the NGINX core requires their appearance otherwise you are seeing the following error\nwhile starting NGINX:\n\n\n    nginx: [emerg] no ssl configured for the server\n\n\nThis directive requires OpenSSL 1.0.2e or greater.\n\nIf you are using the [official pre-built\npackages](https://openresty.org/en/linux-packages.html) for\n[OpenResty](https://openresty.org/) 1.9.7.2 or later, then everything should\nwork out of the box.\n\nIf you are not using the Nginx core shipped with\n[OpenResty](https://openresty.org) 1.9.7.2 or later, you will need to apply\npatches to the standard Nginx core:\n\n<https://openresty.org/en/nginx-ssl-patches.html>\n\n**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)\n\nThis directive was first introduced in the `v0.10.0` release.\n\n[Back to TOC](#directives)\n\nssl_certificate_by_lua_file\n---------------------------\n\n**syntax:** *ssl_certificate_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *server*\n\n**phase:** *right-before-SSL-handshake*\n\nEquivalent to [ssl_certificate_by_lua_block](#ssl_certificate_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nWhen 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.\n\n**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)\n\nThis directive was first introduced in the `v0.10.0` release.\n\n[Back to TOC](#directives)\n\nssl_session_fetch_by_lua_block\n------------------------------\n\n**syntax:** *ssl_session_fetch_by_lua_block { lua-script }*\n\n**context:** *http*\n\n**phase:** *right-before-SSL-handshake*\n\nThis directive runs Lua code to look up and load the SSL session (if any) according to the session ID\nprovided by the current SSL handshake request for the downstream.\n\nThe Lua API for obtaining the current session ID and loading a cached SSL session data\nis provided in the [ngx.ssl.session](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/session.md)\nLua module shipped with the [lua-resty-core](https://github.com/openresty/lua-resty-core#readme)\nlibrary.\n\nLua APIs that may yield, like [ngx.sleep](#ngxsleep) and [cosockets](#ngxsockettcp),\nare enabled in this context.\n\nThis hook, together with the [ssl_session_store_by_lua*](#ssl_session_store_by_lua_block) hook,\ncan be used to implement distributed caching mechanisms in pure Lua (based\non the [cosocket](#ngxsockettcp) API, for example). If a cached SSL session is found\nand loaded into the current SSL connection context,\nSSL session resumption can then get immediately initiated and bypass the full SSL handshake process which is very expensive in terms of CPU time.\n\nPlease note that TLS session tickets are very different and it is the clients' responsibility\nto cache the SSL session state when session tickets are used. SSL session resumptions based on\nTLS session tickets would happen automatically without going through this hook (nor the\n[ssl_session_store_by_lua*](#ssl_session_store_by_lua_block) hook). This hook is mainly\nfor older or less capable SSL clients that can only do SSL sessions by session IDs.\n\nWhen [ssl_certificate_by_lua*](#ssl_certificate_by_lua_block) is specified at the same time,\nthis hook usually runs before [ssl_certificate_by_lua*](#ssl_certificate_by_lua_block).\nWhen the SSL session is found and successfully loaded for the current SSL connection,\nSSL session resumption will happen and thus bypass the [ssl_certificate_by_lua*](#ssl_certificate_by_lua_block)\nhook completely. In this case, Nginx also bypasses the [ssl_session_store_by_lua*](#ssl_session_store_by_lua_block)\nhook, for obvious reasons.\n\nTo easily test this hook locally with a modern web browser, you can temporarily put the following line\nin your https server block to disable the TLS session ticket support:\n\n    ssl_session_tickets off;\n\nBut do not forget to comment this line out before publishing your site to the world.\n\nIf you are using the [official pre-built packages](https://openresty.org/en/linux-packages.html) for [OpenResty](https://openresty.org/)\n1.11.2.1 or later, then everything should work out of the box.\n\nIf you are not using one of the [OpenSSL\npackages](https://openresty.org/en/linux-packages.html) provided by\n[OpenResty](https://openresty.org), you will need to apply patches to OpenSSL\nin order to use this directive:\n\n<https://openresty.org/en/openssl-patches.html>\n\nSimilarly, if you are not using the Nginx core shipped with\n[OpenResty](https://openresty.org) 1.11.2.1 or later, you will need to apply\npatches to the standard Nginx core:\n\n<https://openresty.org/en/nginx-ssl-patches.html>\n\nThis directive was first introduced in the `v0.10.6` release.\n\nNote that this directive can only be used in the **http context** starting\nwith the `v0.10.7` release since SSL session resumption happens\nbefore server name dispatch.\n\n[Back to TOC](#directives)\n\nssl_session_fetch_by_lua_file\n-----------------------------\n\n**syntax:** *ssl_session_fetch_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *http*\n\n**phase:** *right-before-SSL-handshake*\n\nEquivalent to [ssl_session_fetch_by_lua_block](#ssl_session_fetch_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or rather, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nWhen 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.\n\nThis directive was first introduced in the `v0.10.6` release.\n\nNote that: this directive is only allowed to used in **http context** from the `v0.10.7` release\n(because SSL session resumption happens before server name dispatch).\n\n[Back to TOC](#directives)\n\nssl_session_store_by_lua_block\n------------------------------\n\n**syntax:** *ssl_session_store_by_lua_block { lua-script }*\n\n**context:** *http*\n\n**phase:** *right-after-SSL-handshake*\n\nThis directive runs Lua code to fetch and save the SSL session (if any) according to the session ID\nprovided by the current SSL handshake request for the downstream. The saved or cached SSL\nsession data can be used for future SSL connections to resume SSL sessions without going\nthrough the full SSL handshake process (which is very expensive in terms of CPU time).\n\nLua APIs that may yield, like [ngx.sleep](#ngxsleep) and [cosockets](#ngxsockettcp),\nare *disabled* in this context. You can still, however, use the [ngx.timer.at](#ngxtimerat) API\nto create 0-delay timers to save the SSL session data asynchronously to external services (like `redis` or `memcached`).\n\nThe Lua API for obtaining the current session ID and the associated session state data\nis provided in the [ngx.ssl.session](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/session.md#readme)\nLua module shipped with the [lua-resty-core](https://github.com/openresty/lua-resty-core#readme)\nlibrary.\n\nTo easily test this hook locally with a modern web browser, you can temporarily put the following line\nin your https server block to disable the TLS session ticket support:\n\n    ssl_session_tickets off;\n\nBut do not forget to comment this line out before publishing your site to the world.\n\nThis directive was first introduced in the `v0.10.6` release.\n\nNote that: this directive is only allowed to used in **http context** from the `v0.10.7` release\n(because SSL session resumption happens before server name dispatch).\n\n[Back to TOC](#directives)\n\nssl_session_store_by_lua_file\n-----------------------------\n\n**syntax:** *ssl_session_store_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *http*\n\n**phase:** *right-after-SSL-handshake*\n\nEquivalent to [ssl_session_store_by_lua_block](#ssl_session_store_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or rather, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nWhen 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.\n\nThis directive was first introduced in the `v0.10.6` release.\n\nNote that: this directive is only allowed to used in **http context** from the `v0.10.7` release\n(because SSL session resumption happens before server name dispatch).\n\n[Back to TOC](#directives)\n\nproxy_ssl_certificate_by_lua_block\n----------------------------------\n\n**syntax:** *proxy_ssl_certificate_by_lua_block { lua-script }*\n\n**context:** *location*\n\n**phase:** *right-after-server-certificate-request-message-was-processed*\n\nThis directive runs user Lua code when Nginx is about to post-process the SSL server certificate request message from upstream. It is particularly useful for setting the SSL certificate chain and the corresponding private key for the upstream SSL (https) connections. It is also useful to load such handshake configurations nonblockingly from the remote (for example, with the [cosocket](#ngxsockettcp) API).\n\nThe [ngx.ssl.proxysslcert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslcert.md) Lua module provided by the [lua-resty-core](https://github.com/openresty/lua-resty-core/#readme) library are particularly useful in this context.\n\nBelow is a trivial example using the [ngx.ssl](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md) module and the [ngx.ssl.proxysslcert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslcert.md) module at the same time:\n\n```nginx\n\n server {\n     listen 443 ssl;\n     server_name   test.com;\n     ssl_certificate /path/to/cert.crt;\n     ssl_certificate_key /path/to/key.key;\n\n     location /t {\n         proxy_pass https://upstream;\n\n         proxy_ssl_certificate_by_lua_block {\n             local ssl = require \"ngx.ssl\"\n             local proxy_ssl_cert = require \"ngx.ssl.proxysslcert\"\n\n             -- NOTE: for illustration only, we don't handle error below\n\n             local f = assert(io.open(\"/path/to/cert.crt\"))\n             local cert_data = f:read(\"*a\")\n             f:close()\n\n             local cert, err = ssl.parse_pem_cert(cert_data)\n             local ok, err = proxy_ssl_cert.set_cert(cert)\n\n             local f = assert(io.open(\"/path/to/key.key\"))\n             local pkey_data = f:read(\"*a\")\n             f:close()\n\n             local pkey, err = ssl.parse_pem_priv_key(pkey_data)\n             local ok, err = proxy_ssl_cert.set_priv_key(pkey)\n             -- ...\n        }\n     }\n     ...\n }\n```\n\nSee more information in the [ngx.ssl.proxysslcert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslcert.md) Lua module's official documentation.\n\nUncaught Lua exceptions in the user Lua code immediately abort the current SSL session, so does the\n[ngx.exit](#ngxexit) call with an error code like `ngx.ERROR`.\n\nThis Lua code execution context *does* support yielding, so Lua APIs that may yield (like cosockets, sleeping, and \"light threads\") are enabled in this context.\n\nNote that, unlike the relations between 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) directives and [ssl_certificate_by_lua*](#ssl_certificate_by_lua_block), the [proxy_ssl_certificate](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate) and [proxy_ssl_certificate_key](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate_key) directives can be used together with [proxy_ssl_certificate_by_lua*](#proxy_ssl_certificate_by_lua_block).\n\n* When there are only [proxy_ssl_certificate](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate) and [proxy_ssl_certificate_key](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate_key) directives, the original Nginx behavior will obviously remain the same.\n\n* When there is only [proxy_ssl_certificate_by_lua*](#proxy_ssl_certificate_by_lua_block), Nginx will send the certificate and its related private key and chain set by Lua codes.\n\n* When the [proxy_ssl_certificate](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate) and [proxy_ssl_certificate_key](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate_key) directives and [proxy_ssl_certificate_by_lua*](#proxy_ssl_certificate_by_lua_block) are used at the same time, then [proxy_ssl_certificate_by_lua*](#proxy_ssl_certificate_by_lua_block) will take precedence over the [proxy_ssl_certificate](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate) and [proxy_ssl_certificate_key](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate_key) directives.\n\nPlease refer to corresponding test case file and [ngx.ssl.proxysslcert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslcert.md) for more details.\n\nNote also that, it has the same condition as the [proxy_ssl_certificate](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate) directive for [proxy_ssl_certificate_by_lua*](#proxy_ssl_certificate_by_lua_block) to work, that is the upstream server should enable verification of client certificates.\n\nThis directive requires OpenSSL 1.0.2e or greater.\n\n[Back to TOC](#directives)\n\nproxy_ssl_certificate_by_lua_file\n---------------------------------\n\n**syntax:** *proxy_ssl_certificate_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *location*\n\n**phase:** *right-after-server-certificate-request-message-was-processed*\n\nEquivalent to [proxy_ssl_certificate_by_lua_block](#proxy_ssl_certificate_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nWhen 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.\n\n[Back to TOC](#directives)\n\nproxy_ssl_verify_by_lua_block\n-----------------------------\n\n**syntax:** *proxy_ssl_verify_by_lua_block { lua-script }*\n\n**context:** *location*\n\n**phase:** *right-after-server-certificate-message-was-processed*\n\nThis directive runs user Lua code when Nginx is about to post-process the SSL server certificate message for the upstream SSL (https) connections.\n\nIt is particularly useful to parse upstream server certificate and do some custom operations in pure lua.\n\nThe [ngx.ssl.proxysslverify](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslverify.md) Lua module provided by the [lua-resty-core](https://github.com/openresty/lua-resty-core/#readme)\nlibrary are particularly useful in this context.\n\nBelow is a trivial example using the\n[ngx.ssl.proxysslverify](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslverify.md) module\nat the same time:\n\n```nginx\n\n server {\n     listen 443 ssl;\n     server_name   test.com;\n     ssl_certificate /path/to/cert.crt;\n     ssl_certificate_key /path/to/key.key;\n\n     location /t {\n         proxy_ssl_certificate /path/to/cert.crt;\n         proxy_ssl_certificate_key /path/to/key.key;\n         proxy_pass https://upstream;\n\n         proxy_ssl_verify_by_lua_block {\n             local proxy_ssl_vfy = require \"ngx.ssl.proxysslverify\"\n             local cert = proxy_ssl_vfy.get_verify_cert()\n\n             -- ocsp to verify cert\n             -- check crl\n             proxy_ssl_vfy.set_verify_result()\n             ...\n         }\n     }\n     ...\n }\n```\n\nSee more information in the [ngx.ssl.proxysslverify](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslverify.md)\nLua module's official documentation.\n\nUncaught Lua exceptions in the user Lua code immediately abort the current SSL session, so does the\n[ngx.exit](#ngxexit) call with an error code like `ngx.ERROR`.\n\nThis Lua code execution context *does* support yielding, so Lua APIs that may yield\n(like cosockets, sleeping, and \"light threads\")\nare enabled in this context\n\nThis directive requires OpenSSL 3.0.2 or greater.\n\n[Back to TOC](#directives)\n\nproxy_ssl_verify_by_lua_file\n----------------------------\n\n**syntax:** *proxy_ssl_verify_by_lua_file &lt;path-to-lua-script-file&gt;*\n\n**context:** *location*\n\n**phase:** *right-after-server-certificate-message-was-processed*\n\nEquivalent to [proxy_ssl_verify_by_lua_block](#proxy_ssl_verify_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.\n\nWhen 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.\n\n[Back to TOC](#directives)\n\nlua_shared_dict\n---------------\n\n**syntax:** *lua_shared_dict &lt;name&gt; &lt;size&gt;*\n\n**default:** *no*\n\n**context:** *http*\n\n**phase:** *depends on usage*\n\nDeclares a shared memory zone, `<name>`, to serve as storage for the shm based Lua dictionary `ngx.shared.<name>`.\n\nShared memory zones are always shared by all the Nginx worker processes in the current Nginx server instance.\n\nThe `<size>` argument accepts size units such as `k` and `m`:\n\n```nginx\n\n http {\n     lua_shared_dict dogs 10m;\n     ...\n }\n```\n\nThe hard-coded minimum size is 8KB while the practical minimum size depends\non actual user data set (some people start with 12KB).\n\nSee [ngx.shared.DICT](#ngxshareddict) for details.\n\nThis directive was first introduced in the `v0.3.1rc22` release.\n\n[Back to TOC](#directives)\n\nlua_socket_connect_timeout\n--------------------------\n\n**syntax:** *lua_socket_connect_timeout &lt;time&gt;*\n\n**default:** *lua_socket_connect_timeout 60s*\n\n**context:** *http, server, location*\n\nThis directive controls the default timeout value used in TCP/unix-domain socket object's [connect](#tcpsockconnect) method and can be overridden by the [settimeout](#tcpsocksettimeout) or [settimeouts](#tcpsocksettimeouts) methods.\n\nThe `<time>` argument can be an integer, with an optional time unit, like `s` (second), `ms` (millisecond), `m` (minute). The default time unit is `s`, i.e., \"second\". The default setting is `60s`.\n\nThis directive was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#directives)\n\nlua_socket_send_timeout\n-----------------------\n\n**syntax:** *lua_socket_send_timeout &lt;time&gt;*\n\n**default:** *lua_socket_send_timeout 60s*\n\n**context:** *http, server, location*\n\nControls the default timeout value used in TCP/unix-domain socket object's [send](#tcpsocksend) method and can be overridden by the [settimeout](#tcpsocksettimeout) or [settimeouts](#tcpsocksettimeouts) methods.\n\nThe `<time>` argument can be an integer, with an optional time unit, like `s` (second), `ms` (millisecond), `m` (minute). The default time unit is `s`, i.e., \"second\". The default setting is `60s`.\n\nThis directive was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#directives)\n\nlua_socket_send_lowat\n---------------------\n\n**syntax:** *lua_socket_send_lowat &lt;size&gt;*\n\n**default:** *lua_socket_send_lowat 0*\n\n**context:** *http, server, location*\n\nControls the `lowat` (low water) value for the cosocket send buffer.\n\n[Back to TOC](#directives)\n\nlua_socket_read_timeout\n-----------------------\n\n**syntax:** *lua_socket_read_timeout &lt;time&gt;*\n\n**default:** *lua_socket_read_timeout 60s*\n\n**context:** *http, server, location*\n\n**phase:** *depends on usage*\n\nThis directive controls the default timeout value used in TCP/unix-domain socket object's [receive](#tcpsockreceive) method and iterator functions returned by the [receiveuntil](#tcpsockreceiveuntil) method. This setting can be overridden by the [settimeout](#tcpsocksettimeout) or [settimeouts](#tcpsocksettimeouts) methods.\n\nThe `<time>` argument can be an integer, with an optional time unit, like `s` (second), `ms` (millisecond), `m` (minute). The default time unit is `s`, i.e., \"second\". The default setting is `60s`.\n\nThis directive was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#directives)\n\nlua_socket_buffer_size\n----------------------\n\n**syntax:** *lua_socket_buffer_size &lt;size&gt;*\n\n**default:** *lua_socket_buffer_size 4k/8k*\n\n**context:** *http, server, location*\n\nSpecifies the buffer size used by cosocket reading operations.\n\nThis buffer does not have to be that big to hold everything at the same time because cosocket supports 100% non-buffered reading and parsing. So even `1` byte buffer size should still work everywhere but the performance could be terrible.\n\nThis directive was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#directives)\n\nlua_socket_pool_size\n--------------------\n\n**syntax:** *lua_socket_pool_size &lt;size&gt;*\n\n**default:** *lua_socket_pool_size 30*\n\n**context:** *http, server, location*\n\nSpecifies the size limit (in terms of connection count) for every cosocket connection pool associated with every remote server (i.e., identified by either the host-port pair or the unix domain socket file path).\n\nDefault to 30 connections for every pool.\n\nWhen the connection pool exceeds the available size limit, the least recently used (idle) connection already in the pool will be closed to make room for the current connection.\n\nNote that the cosocket connection pool is per Nginx worker process rather than per Nginx server instance, so size limit specified here also applies to every single Nginx worker process.\n\nThis directive was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#directives)\n\nlua_socket_keepalive_timeout\n----------------------------\n\n**syntax:** *lua_socket_keepalive_timeout &lt;time&gt;*\n\n**default:** *lua_socket_keepalive_timeout 60s*\n\n**context:** *http, server, location*\n\nThis directive controls the default maximal idle time of the connections in the cosocket built-in connection pool. When this timeout reaches, idle connections will be closed and removed from the pool. This setting can be overridden by cosocket objects' [setkeepalive](#tcpsocksetkeepalive) method.\n\nThe `<time>` argument can be an integer, with an optional time unit, like `s` (second), `ms` (millisecond), `m` (minute). The default time unit is `s`, i.e., \"second\". The default setting is `60s`.\n\nThis directive was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#directives)\n\nlua_socket_log_errors\n---------------------\n\n**syntax:** *lua_socket_log_errors on|off*\n\n**default:** *lua_socket_log_errors on*\n\n**context:** *http, server, location*\n\nThis 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).\n\nThis directive was first introduced in the `v0.5.13` release.\n\n[Back to TOC](#directives)\n\nlua_ssl_ciphers\n---------------\n\n**syntax:** *lua_ssl_ciphers &lt;ciphers&gt;*\n\n**default:** *lua_ssl_ciphers DEFAULT*\n\n**context:** *http, server, location*\n\nSpecifies 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.\n\nThe full list can be viewed using the “openssl ciphers” command.\n\nThis directive was first introduced in the `v0.9.11` release.\n\n[Back to TOC](#directives)\n\nlua_ssl_crl\n-----------\n\n**syntax:** *lua_ssl_crl &lt;file&gt;*\n\n**default:** *no*\n\n**context:** *http, server, location*\n\nSpecifies 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.\n\nThis directive was first introduced in the `v0.9.11` release.\n\n[Back to TOC](#directives)\n\nlua_ssl_protocols\n-----------------\n\n**syntax:** *lua_ssl_protocols \\[SSLv2\\] \\[SSLv3\\] \\[TLSv1\\] [TLSv1.1] [TLSv1.2] [TLSv1.3]*\n\n**default:** *lua_ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3*\n\n**context:** *http, server, location*\n\nEnables the specified protocols for requests to a SSL/TLS server in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method.\n\nThe support for the `TLSv1.3` parameter requires version `v0.10.12` *and* OpenSSL 1.1.1.\nFrom version v0.10.25, the default value change from `SSLV3 TLSv1 TLSv1.1 TLSv1.2` to `TLSv1 TLSv1.1 TLSv1.2 TLSv1.3`.\n\nThis directive was first introduced in the `v0.9.11` release.\n\n[Back to TOC](#directives)\n\nlua_ssl_certificate\n-------------------\n\n**syntax:** *lua_ssl_certificate &lt;file&gt;*\n\n**default:** *none*\n\n**context:** *http, server, location*\n\nSpecifies the file path to the SSL/TLS certificate in PEM format used for the [tcpsock:sslhandshake](#tcpsocksslhandshake) method.\n\nThis directive allows you to specify the SSL/TLS certificate that will be presented to server during the SSL/TLS handshake process.\n\nThis directive was first introduced in the `v0.10.26` release.\n\nSee also [lua_ssl_certificate_key](#lua_ssl_certificate_key) and [lua_ssl_verify_depth](#lua_ssl_verify_depth).\n\n[Back to TOC](#directives)\n\nlua_ssl_certificate_key\n-----------------------\n\n**syntax:** *lua_ssl_certificate_key &lt;file&gt;*\n\n**default:** *none*\n\n**context:** *http, server, location*\n\nSpecifies the file path to the private key associated with the SSL/TLS certificate used in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method.\n\nThis 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.\n\nThis directive was first introduced in the `v0.10.26` release.\n\nSee also [lua_ssl_certificate](#lua_ssl_certificate) and [lua_ssl_verify_depth](#lua_ssl_verify_depth).\n\n[Back to TOC](#directives)\n\nlua_ssl_trusted_certificate\n---------------------------\n\n**syntax:** *lua_ssl_trusted_certificate &lt;file&gt;*\n\n**default:** *none*\n\n**context:** *http, server, location*\n\nSpecifies 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.\n\nThis directive was first introduced in the `v0.9.11` release.\n\nSee also [lua_ssl_verify_depth](#lua_ssl_verify_depth).\n\n[Back to TOC](#directives)\n\nlua_ssl_verify_depth\n--------------------\n\n**syntax:** *lua_ssl_verify_depth &lt;number&gt;*\n\n**default:** *lua_ssl_verify_depth 1*\n\n**context:** *http, server, location*\n\nSets the verification depth in the server certificates chain.\n\nThis directive was first introduced in the `v0.9.11` release.\n\nSee also [lua_ssl_certificate](#lua_ssl_certificate), [lua_ssl_certificate_key](#lua_ssl_certificate_key) and [lua_ssl_trusted_certificate](#lua_ssl_trusted_certificate).\n\n[Back to TOC](#directives)\n\nlua_ssl_key_log\n---------------\n\n**syntax:** *lua_ssl_key_log &lt;file&gt;*\n\n**default:** *none*\n\n**context:** *http, server, location*\n\nEnables 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.\n\n[Back to TOC](#directives)\n\nlua_ssl_conf_command\n--------------------\n\n**syntax:** *lua_ssl_conf_command &lt;command&gt;*\n\n**default:** *no*\n\n**context:** *http, server, location*\n\nSets arbitrary OpenSSL configuration [commands](https://www.openssl.org/docs/man1.1.1/man3/SSL_CONF_cmd.html).\n\nThe 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.\n\nSeveral `lua_ssl_conf_command` directives can be specified on the same level:\n\n```nginx\n\n lua_ssl_conf_command Options PrioritizeChaCha;\n lua_ssl_conf_command Ciphersuites TLS_CHACHA20_POLY1305_SHA256;\n```\n\nConfiguration commands are applied after OpenResty own configuration for SSL, so they can be used to override anything set by OpenResty.\n\nNote 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.\n\nThis directive was first introduced in the `v0.10.21` release.\n\n\n\n[Back to TOC](#directives)\n\nlua_upstream_skip_openssl_default_verify\n--------------------\n\n**syntax:** *lua_upstream_skip_openssl_default_verify on|off*\n\n**default:** *lua_upstream_skip_openssl_default_verify off*\n\n**context:** *location, location-if*\n\nWhen 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.\n\nThis directive is turned `off` by default.\n\n[Back to TOC](#directives)\n\nlua_http10_buffering\n--------------------\n\n**syntax:** *lua_http10_buffering on|off*\n\n**default:** *lua_http10_buffering on*\n\n**context:** *http, server, location, location-if*\n\nEnables or disables automatic response buffering for HTTP 1.0 (or older) requests. This buffering mechanism is mainly used for HTTP 1.0 keep-alive which relies on a proper `Content-Length` response header.\n\nIf the Lua code explicitly sets a `Content-Length` response header before sending the headers (either explicitly via [ngx.send_headers](#ngxsend_headers) or implicitly via the first [ngx.say](#ngxsay) or [ngx.print](#ngxprint) call), then the HTTP 1.0 response buffering will be disabled even when this directive is turned on.\n\nTo output very large response data in a streaming fashion (via the [ngx.flush](#ngxflush) call, for example), this directive MUST be turned off to minimize memory usage.\n\nThis directive is turned `on` by default.\n\nThis directive was first introduced in the `v0.5.0rc19` release.\n\n[Back to TOC](#directives)\n\nrewrite_by_lua_no_postpone\n--------------------------\n\n**syntax:** *rewrite_by_lua_no_postpone on|off*\n\n**default:** *rewrite_by_lua_no_postpone off*\n\n**context:** *http*\n\nControls whether or not to disable postponing [rewrite_by_lua*](#rewrite_by_lua) directives to run at the end of the `rewrite` request-processing phase. By default, this directive is turned off and the Lua code is postponed to run at the end of the `rewrite` phase.\n\nThis directive was first introduced in the `v0.5.0rc29` release.\n\n[Back to TOC](#directives)\n\naccess_by_lua_no_postpone\n-------------------------\n\n**syntax:** *access_by_lua_no_postpone on|off*\n\n**default:** *access_by_lua_no_postpone off*\n\n**context:** *http*\n\nControls whether or not to disable postponing [access_by_lua*](#access_by_lua) directives to run at the end of the `access` request-processing phase. By default, this directive is turned off and the Lua code is postponed to run at the end of the `access` phase.\n\nThis directive was first introduced in the `v0.9.20` release.\n\n[Back to TOC](#directives)\n\nprecontent_by_lua_no_postpone\n-------------------------\n\n**syntax:** *precontent_by_lua_no_postpone on|off*\n\n**default:** *precontent_by_lua_no_postpone off*\n\n**context:** *http*\n\nControls whether or not to disable postponing [precontent_by_lua*](#precontent_by_lua_block) directives to run at the end of the `precontent` request-processing phase. By default, this directive is turned off and the Lua code is postponed to run at the end of the `precontent` phase.\n\n[Back to TOC](#directives)\n\nlua_transform_underscores_in_response_headers\n---------------------------------------------\n\n**syntax:** *lua_transform_underscores_in_response_headers on|off*\n\n**default:** *lua_transform_underscores_in_response_headers on*\n\n**context:** *http, server, location, location-if*\n\nControls whether to transform underscores (`_`) in the response header names specified in the [ngx.header.HEADER](#ngxheaderheader) API to hyphens (`-`).\n\nThis directive was first introduced in the `v0.5.0rc32` release.\n\n[Back to TOC](#directives)\n\nlua_check_client_abort\n----------------------\n\n**syntax:** *lua_check_client_abort on|off*\n\n**default:** *lua_check_client_abort off*\n\n**context:** *http, server, location, location-if*\n\nThis directive controls whether to check for premature client connection abortion.\n\nWhen this directive is on, the ngx_lua module will monitor the premature connection close event on the downstream connections and when there is such an event, it will call the user Lua function callback (registered by [ngx.on_abort](#ngxon_abort)) or just stop and clean up all the Lua \"light threads\" running in the current request's request handler when there is no user callback function registered.\n\nAccording to the current implementation, however, if the client closes the connection before the Lua code finishes reading the request body data via [ngx.req.socket](#ngxreqsocket), then ngx_lua will neither stop all the running \"light threads\" nor call the user callback (if [ngx.on_abort](#ngxon_abort) has been called). Instead, the reading operation on [ngx.req.socket](#ngxreqsocket) will just return the error message \"client aborted\" as the second return value (the first return value is surely `nil`).\n\nWhen TCP keepalive is disabled, it is relying on the client side to close the socket gracefully (by sending a `FIN` packet or something like that). For (soft) real-time web applications, it is highly recommended to configure the [TCP keepalive](http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html) support in your system's TCP stack implementation in order to detect \"half-open\" TCP connections in time.\n\nFor example, on Linux, you can configure the standard [listen](http://nginx.org/en/docs/http/ngx_http_core_module.html#listen) directive in your `nginx.conf` file like this:\n\n```nginx\n\n listen 80 so_keepalive=2s:2s:8;\n```\n\nOn FreeBSD, you can only tune the system-wide configuration for TCP keepalive, for example:\n\n    # sysctl net.inet.tcp.keepintvl=2000\n    # sysctl net.inet.tcp.keepidle=2000\n\nThis directive was first introduced in the `v0.7.4` release.\n\nSee also [ngx.on_abort](#ngxon_abort).\n\n[Back to TOC](#directives)\n\nlua_max_pending_timers\n----------------------\n\n**syntax:** *lua_max_pending_timers &lt;count&gt;*\n\n**default:** *lua_max_pending_timers 1024*\n\n**context:** *http*\n\nControls the maximum number of pending timers allowed.\n\nPending timers are those timers that have not expired yet.\n\nWhen exceeding this limit, the [ngx.timer.at](#ngxtimerat) call will immediately return `nil` and the error string \"too many pending timers\".\n\nThis directive was first introduced in the `v0.8.0` release.\n\n[Back to TOC](#directives)\n\nlua_max_running_timers\n----------------------\n\n**syntax:** *lua_max_running_timers &lt;count&gt;*\n\n**default:** *lua_max_running_timers 256*\n\n**context:** *http*\n\nControls the maximum number of \"running timers\" allowed.\n\nRunning timers are those timers whose user callback functions are still running or `lightthreads` spawned in callback functions are still running.\n\nWhen 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.\n\nThis directive was first introduced in the `v0.8.0` release.\n\n[Back to TOC](#directives)\n\nlua_sa_restart\n--------------\n\n**syntax:** *lua_sa_restart on|off*\n\n**default:** *lua_sa_restart on*\n\n**context:** *http*\n\nWhen enabled, this module will set the `SA_RESTART` flag on Nginx workers signal dispositions.\n\nThis allows Lua I/O primitives to not be interrupted by Nginx's handling of various signals.\n\nThis directive was first introduced in the `v0.10.14` release.\n\n[Back to TOC](#directives)\n\nlua_worker_thread_vm_pool_size\n------------------------------\n\n**syntax:** *lua_worker_thread_vm_pool_size &lt;size&gt;*\n\n**default:** *lua_worker_thread_vm_pool_size 10*\n\n**context:** *http*\n\nSpecifies the size limit of the Lua VM pool (default 100) that will be used in the [ngx.run_worker_thread](#ngxrun_worker_thread) API.\n\nAlso, it is not allowed to create Lua VMs that exceeds the pool size limit.\n\nThe Lua VM in the VM pool is used to execute Lua code in separate thread.\n\nThe pool is global at Nginx worker level. And it is used to reuse Lua VMs between requests.\n\n**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.\n\n[Back to TOC](#directives)\n\nNginx API for Lua\n=================\n\n* [Introduction](#introduction)\n* [ngx.arg](#ngxarg)\n* [ngx.var.VARIABLE](#ngxvarvariable)\n* [Core constants](#core-constants)\n* [HTTP method constants](#http-method-constants)\n* [HTTP status constants](#http-status-constants)\n* [Nginx log level constants](#nginx-log-level-constants)\n* [print](#print)\n* [ngx.ctx](#ngxctx)\n* [ngx.location.capture](#ngxlocationcapture)\n* [ngx.location.capture_multi](#ngxlocationcapture_multi)\n* [ngx.status](#ngxstatus)\n* [ngx.header.HEADER](#ngxheaderheader)\n* [ngx.resp.get_headers](#ngxrespget_headers)\n* [ngx.req.is_internal](#ngxreqis_internal)\n* [ngx.req.start_time](#ngxreqstart_time)\n* [ngx.req.http_version](#ngxreqhttp_version)\n* [ngx.req.raw_header](#ngxreqraw_header)\n* [ngx.req.get_method](#ngxreqget_method)\n* [ngx.req.set_method](#ngxreqset_method)\n* [ngx.req.set_uri](#ngxreqset_uri)\n* [ngx.req.set_uri_args](#ngxreqset_uri_args)\n* [ngx.req.get_uri_args](#ngxreqget_uri_args)\n* [ngx.req.get_post_args](#ngxreqget_post_args)\n* [ngx.req.get_headers](#ngxreqget_headers)\n* [ngx.req.set_header](#ngxreqset_header)\n* [ngx.req.clear_header](#ngxreqclear_header)\n* [ngx.req.read_body](#ngxreqread_body)\n* [ngx.req.discard_body](#ngxreqdiscard_body)\n* [ngx.req.get_body_data](#ngxreqget_body_data)\n* [ngx.req.get_body_file](#ngxreqget_body_file)\n* [ngx.req.set_body_data](#ngxreqset_body_data)\n* [ngx.req.set_body_file](#ngxreqset_body_file)\n* [ngx.req.init_body](#ngxreqinit_body)\n* [ngx.req.append_body](#ngxreqappend_body)\n* [ngx.req.finish_body](#ngxreqfinish_body)\n* [ngx.req.socket](#ngxreqsocket)\n* [ngx.exec](#ngxexec)\n* [ngx.redirect](#ngxredirect)\n* [ngx.send_headers](#ngxsend_headers)\n* [ngx.headers_sent](#ngxheaders_sent)\n* [ngx.print](#ngxprint)\n* [ngx.say](#ngxsay)\n* [ngx.log](#ngxlog)\n* [ngx.flush](#ngxflush)\n* [ngx.exit](#ngxexit)\n* [ngx.eof](#ngxeof)\n* [ngx.sleep](#ngxsleep)\n* [ngx.escape_uri](#ngxescape_uri)\n* [ngx.unescape_uri](#ngxunescape_uri)\n* [ngx.encode_args](#ngxencode_args)\n* [ngx.decode_args](#ngxdecode_args)\n* [ngx.encode_base64](#ngxencode_base64)\n* [ngx.decode_base64](#ngxdecode_base64)\n* [ngx.decode_base64mime](#ngxdecode_base64mime)\n* [ngx.crc32_short](#ngxcrc32_short)\n* [ngx.crc32_long](#ngxcrc32_long)\n* [ngx.hmac_sha1](#ngxhmac_sha1)\n* [ngx.md5](#ngxmd5)\n* [ngx.md5_bin](#ngxmd5_bin)\n* [ngx.sha1_bin](#ngxsha1_bin)\n* [ngx.quote_sql_str](#ngxquote_sql_str)\n* [ngx.today](#ngxtoday)\n* [ngx.time](#ngxtime)\n* [ngx.now](#ngxnow)\n* [ngx.update_time](#ngxupdate_time)\n* [ngx.localtime](#ngxlocaltime)\n* [ngx.utctime](#ngxutctime)\n* [ngx.cookie_time](#ngxcookie_time)\n* [ngx.http_time](#ngxhttp_time)\n* [ngx.parse_http_time](#ngxparse_http_time)\n* [ngx.is_subrequest](#ngxis_subrequest)\n* [ngx.re.match](#ngxrematch)\n* [ngx.re.find](#ngxrefind)\n* [ngx.re.gmatch](#ngxregmatch)\n* [ngx.re.sub](#ngxresub)\n* [ngx.re.gsub](#ngxregsub)\n* [ngx.shared.DICT](#ngxshareddict)\n* [ngx.shared.DICT.get](#ngxshareddictget)\n* [ngx.shared.DICT.get_stale](#ngxshareddictget_stale)\n* [ngx.shared.DICT.set](#ngxshareddictset)\n* [ngx.shared.DICT.safe_set](#ngxshareddictsafe_set)\n* [ngx.shared.DICT.add](#ngxshareddictadd)\n* [ngx.shared.DICT.safe_add](#ngxshareddictsafe_add)\n* [ngx.shared.DICT.replace](#ngxshareddictreplace)\n* [ngx.shared.DICT.delete](#ngxshareddictdelete)\n* [ngx.shared.DICT.incr](#ngxshareddictincr)\n* [ngx.shared.DICT.lpush](#ngxshareddictlpush)\n* [ngx.shared.DICT.rpush](#ngxshareddictrpush)\n* [ngx.shared.DICT.lpop](#ngxshareddictlpop)\n* [ngx.shared.DICT.rpop](#ngxshareddictrpop)\n* [ngx.shared.DICT.llen](#ngxshareddictllen)\n* [ngx.shared.DICT.ttl](#ngxshareddictttl)\n* [ngx.shared.DICT.expire](#ngxshareddictexpire)\n* [ngx.shared.DICT.flush_all](#ngxshareddictflush_all)\n* [ngx.shared.DICT.flush_expired](#ngxshareddictflush_expired)\n* [ngx.shared.DICT.get_keys](#ngxshareddictget_keys)\n* [ngx.shared.DICT.capacity](#ngxshareddictcapacity)\n* [ngx.shared.DICT.free_space](#ngxshareddictfree_space)\n* [ngx.socket.udp](#ngxsocketudp)\n* [udpsock:bind](#udpsockbind)\n* [udpsock:setpeername](#udpsocksetpeername)\n* [udpsock:send](#udpsocksend)\n* [udpsock:receive](#udpsockreceive)\n* [udpsock:close](#udpsockclose)\n* [udpsock:settimeout](#udpsocksettimeout)\n* [ngx.socket.stream](#ngxsocketstream)\n* [ngx.socket.tcp](#ngxsockettcp)\n* [tcpsock:bind](#tcpsockbind)\n* [tcpsock:connect](#tcpsockconnect)\n* [tcpsock:getfd](#getfd)\n* [tcpsock:setclientcert](#tcpsocksetclientcert)\n* [tcpsock:sslhandshake](#tcpsocksslhandshake)\n* [tcpsock:send](#tcpsocksend)\n* [tcpsock:receive](#tcpsockreceive)\n* [tcpsock:receiveany](#tcpsockreceiveany)\n* [tcpsock:receiveuntil](#tcpsockreceiveuntil)\n* [tcpsock:close](#tcpsockclose)\n* [tcpsock:settimeout](#tcpsocksettimeout)\n* [tcpsock:settimeouts](#tcpsocksettimeouts)\n* [tcpsock:setoption](#tcpsocksetoption)\n* [tcpsock:setkeepalive](#tcpsocksetkeepalive)\n* [tcpsock:getreusedtimes](#tcpsockgetreusedtimes)\n* [tcpsock:getsslpointer](#tcpsockgetsslpointer)\n* [tcpsock:getsslctx](#tcpsockgetsslctx)\n* [tcpsock:getsslsession](#tcpsockgetsslsession)\n* [ngx.socket.connect](#ngxsocketconnect)\n* [ngx.get_phase](#ngxget_phase)\n* [ngx.thread.spawn](#ngxthreadspawn)\n* [ngx.thread.wait](#ngxthreadwait)\n* [ngx.thread.kill](#ngxthreadkill)\n* [ngx.on_abort](#ngxon_abort)\n* [ngx.timer.at](#ngxtimerat)\n* [ngx.timer.every](#ngxtimerevery)\n* [ngx.timer.running_count](#ngxtimerrunning_count)\n* [ngx.timer.pending_count](#ngxtimerpending_count)\n* [ngx.config.subsystem](#ngxconfigsubsystem)\n* [ngx.config.debug](#ngxconfigdebug)\n* [ngx.config.prefix](#ngxconfigprefix)\n* [ngx.config.nginx_version](#ngxconfignginx_version)\n* [ngx.config.nginx_configure](#ngxconfignginx_configure)\n* [ngx.config.ngx_lua_version](#ngxconfigngx_lua_version)\n* [ngx.worker.exiting](#ngxworkerexiting)\n* [ngx.worker.pid](#ngxworkerpid)\n* [ngx.worker.pids](#ngxworkerpids)\n* [ngx.worker.count](#ngxworkercount)\n* [ngx.worker.id](#ngxworkerid)\n* [ngx.semaphore](#ngxsemaphore)\n* [ngx.balancer](#ngxbalancer)\n* [ngx.ssl](#ngxssl)\n* [ngx.ocsp](#ngxocsp)\n* [ndk.set_var.DIRECTIVE](#ndkset_vardirective)\n* [coroutine.create](#coroutinecreate)\n* [coroutine.resume](#coroutineresume)\n* [coroutine.yield](#coroutineyield)\n* [coroutine.wrap](#coroutinewrap)\n* [coroutine.running](#coroutinerunning)\n* [coroutine.status](#coroutinestatus)\n* [ngx.run_worker_thread](#ngxrun_worker_thread)\n\n\n[Back to TOC](#table-of-contents)\n\nIntroduction\n------------\n\nThe various `*_by_lua`, `*_by_lua_block` and `*_by_lua_file` configuration directives serve as gateways to the Lua API within the `nginx.conf` file. The Nginx Lua API described below can only be called within the user Lua code run in the context of these configuration directives.\n\nThe API is exposed to Lua in the form of two standard packages `ngx` and `ndk`. These packages are in the default global scope within ngx_lua and are always available within ngx_lua directives.\n\nThe packages can be introduced into external Lua modules like this:\n\n```lua\n\n local say = ngx.say\n\n local _M = {}\n\n function _M.foo(a)\n     say(a)\n end\n\n return _M\n```\n\nUse of the [package.seeall](https://www.lua.org/manual/5.1/manual.html#pdf-package.seeall) flag is strongly discouraged due to its various bad side-effects.\n\nIt is also possible to directly require the packages in external Lua modules:\n\n```lua\n\n local ngx = require \"ngx\"\n local ndk = require \"ndk\"\n```\n\nThe ability to require these packages was introduced in the `v0.2.1rc19` release.\n\nNetwork I/O operations in user code should only be done through the Nginx Lua API calls as the Nginx event loop may be blocked and performance drop off dramatically otherwise. Disk operations with relatively small amount of data can be done using the standard Lua `io` library but huge file reading and writing should be avoided wherever possible as they may block the Nginx process significantly. Delegating all network and disk I/O operations to Nginx's subrequests (via the [ngx.location.capture](#ngxlocationcapture) method and similar) is strongly recommended for maximum performance.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.arg\n-------\n\n**syntax:** *val = ngx.arg\\[index\\]*\n\n**context:** *set_by_lua&#42;, body_filter_by_lua&#42;*\n\nWhen this is used in the context of the [set_by_lua*](#set_by_lua) directives, this table is read-only and holds the input arguments to the config directives:\n\n```lua\n\n value = ngx.arg[n]\n```\n\nHere is an example\n\n```nginx\n\n location /foo {\n     set $a 32;\n     set $b 56;\n\n     set_by_lua $sum\n         'return tonumber(ngx.arg[1]) + tonumber(ngx.arg[2])'\n         $a $b;\n\n     echo $sum;\n }\n```\n\nthat writes out `88`, the sum of `32` and `56`.\n\nWhen this table is used in the context of [body_filter_by_lua*](#body_filter_by_lua), the first element holds the input data chunk to the output filter code and the second element holds the boolean flag for the \"eof\" flag indicating the end of the whole output data stream.\n\nThe data chunk and \"eof\" flag passed to the downstream Nginx output filters can also be overridden by assigning values directly to the corresponding table elements. When setting `nil` or an empty Lua string value to `ngx.arg[1]`, no data chunk will be passed to the downstream Nginx output filters at all.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.var.VARIABLE\n----------------\n\n**syntax:** *ngx.var.VAR_NAME*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, balancer_by_lua&#42;*\n\nRead and write Nginx variable values.\n\n```nginx\n\n value = ngx.var.some_nginx_variable_name\n ngx.var.some_nginx_variable_name = value\n```\n\nNote that only already defined Nginx variables can be written to.\nFor example:\n\n```nginx\n\n location /foo {\n     set $my_var ''; # this line is required to create $my_var at config time\n     content_by_lua_block {\n         ngx.var.my_var = 123\n         ...\n     }\n }\n```\n\nThat is, Nginx variables cannot be created on-the-fly. Here is a list of pre-defined\n[Nginx variables](http://nginx.org/en/docs/varindex.html).\n\nSome special Nginx variables like `$args` and `$limit_rate` can be assigned a value,\nmany others are not, like `$query_string`, `$arg_PARAMETER`, and `$http_NAME`.\n\nNginx regex group capturing variables `$1`, `$2`, `$3`, and etc, can be read by this\ninterface as well, by writing `ngx.var[1]`, `ngx.var[2]`, `ngx.var[3]`, and etc.\n\nSetting `ngx.var.Foo` to a `nil` value will unset the `$Foo` Nginx variable.\n\n```lua\n\n ngx.var.args = nil\n```\n\n**CAUTION** When reading from an Nginx variable, Nginx will allocate memory in the per-request memory pool which is freed only at request termination. So when you need to read from an Nginx variable repeatedly in your Lua code, cache the Nginx variable value to your own Lua variable, for example,\n\n```lua\n\n local val = ngx.var.some_var\n --- use the val repeatedly later\n```\n\nto prevent (temporary) memory leaking within the current request's lifetime. Another way of caching the result is to use the [ngx.ctx](#ngxctx) table.\n\nUndefined Nginx variables are evaluated to `nil` while uninitialized (but defined) Nginx variables are evaluated to an empty Lua string.\n\nThis API requires a relatively expensive metamethod call and it is recommended to avoid using it on hot code paths.\n\n[Back to TOC](#nginx-api-for-lua)\n\nCore constants\n--------------\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, &#42;log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\n```lua\n\n   ngx.OK (0)\n   ngx.ERROR (-1)\n   ngx.AGAIN (-2)\n   ngx.DONE (-4)\n   ngx.DECLINED (-5)\n```\n\nNote that only three of these constants are utilized by the [Nginx API for Lua](#nginx-api-for-lua) (i.e., [ngx.exit](#ngxexit) accepts `ngx.OK`, `ngx.ERROR`, and `ngx.DECLINED` as input).\n\n```lua\n\n   ngx.null\n```\n\nThe `ngx.null` constant is a `NULL` light userdata usually used to represent nil values in Lua tables etc and is similar to the [lua-cjson](http://www.kyne.com.au/~mark/software/lua-cjson.php) library's `cjson.null` constant. This constant was first introduced in the `v0.5.0rc5` release.\n\nThe `ngx.DECLINED` constant was first introduced in the `v0.5.0rc19` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nHTTP method constants\n---------------------\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\n\n      ngx.HTTP_GET\n      ngx.HTTP_HEAD\n      ngx.HTTP_PUT\n      ngx.HTTP_POST\n      ngx.HTTP_DELETE\n      ngx.HTTP_OPTIONS   (added in the v0.5.0rc24 release)\n      ngx.HTTP_MKCOL     (added in the v0.8.2 release)\n      ngx.HTTP_COPY      (added in the v0.8.2 release)\n      ngx.HTTP_MOVE      (added in the v0.8.2 release)\n      ngx.HTTP_PROPFIND  (added in the v0.8.2 release)\n      ngx.HTTP_PROPPATCH (added in the v0.8.2 release)\n      ngx.HTTP_LOCK      (added in the v0.8.2 release)\n      ngx.HTTP_UNLOCK    (added in the v0.8.2 release)\n      ngx.HTTP_PATCH     (added in the v0.8.2 release)\n      ngx.HTTP_TRACE     (added in the v0.8.2 release)\n\n\nThese constants are usually used in [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi) method calls.\n\n[Back to TOC](#nginx-api-for-lua)\n\nHTTP status constants\n---------------------\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\n```nginx\n\n   value = ngx.HTTP_CONTINUE (100) (first added in the v0.9.20 release)\n   value = ngx.HTTP_SWITCHING_PROTOCOLS (101) (first added in the v0.9.20 release)\n   value = ngx.HTTP_OK (200)\n   value = ngx.HTTP_CREATED (201)\n   value = ngx.HTTP_ACCEPTED (202) (first added in the v0.9.20 release)\n   value = ngx.HTTP_NO_CONTENT (204) (first added in the v0.9.20 release)\n   value = ngx.HTTP_PARTIAL_CONTENT (206) (first added in the v0.9.20 release)\n   value = ngx.HTTP_SPECIAL_RESPONSE (300)\n   value = ngx.HTTP_MOVED_PERMANENTLY (301)\n   value = ngx.HTTP_MOVED_TEMPORARILY (302)\n   value = ngx.HTTP_SEE_OTHER (303)\n   value = ngx.HTTP_NOT_MODIFIED (304)\n   value = ngx.HTTP_TEMPORARY_REDIRECT (307) (first added in the v0.9.20 release)\n   value = ngx.HTTP_PERMANENT_REDIRECT (308)\n   value = ngx.HTTP_BAD_REQUEST (400)\n   value = ngx.HTTP_UNAUTHORIZED (401)\n   value = ngx.HTTP_PAYMENT_REQUIRED (402) (first added in the v0.9.20 release)\n   value = ngx.HTTP_FORBIDDEN (403)\n   value = ngx.HTTP_NOT_FOUND (404)\n   value = ngx.HTTP_NOT_ALLOWED (405)\n   value = ngx.HTTP_NOT_ACCEPTABLE (406) (first added in the v0.9.20 release)\n   value = ngx.HTTP_REQUEST_TIMEOUT (408) (first added in the v0.9.20 release)\n   value = ngx.HTTP_CONFLICT (409) (first added in the v0.9.20 release)\n   value = ngx.HTTP_GONE (410)\n   value = ngx.HTTP_UPGRADE_REQUIRED (426) (first added in the v0.9.20 release)\n   value = ngx.HTTP_TOO_MANY_REQUESTS (429) (first added in the v0.9.20 release)\n   value = ngx.HTTP_CLOSE (444) (first added in the v0.9.20 release)\n   value = ngx.HTTP_ILLEGAL (451) (first added in the v0.9.20 release)\n   value = ngx.HTTP_INTERNAL_SERVER_ERROR (500)\n   value = ngx.HTTP_NOT_IMPLEMENTED (501)\n   value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) (kept for compatibility)\n   value = ngx.HTTP_BAD_GATEWAY (502) (first added in the v0.9.20 release)\n   value = ngx.HTTP_SERVICE_UNAVAILABLE (503)\n   value = ngx.HTTP_GATEWAY_TIMEOUT (504) (first added in the v0.3.1rc38 release)\n   value = ngx.HTTP_VERSION_NOT_SUPPORTED (505) (first added in the v0.9.20 release)\n   value = ngx.HTTP_INSUFFICIENT_STORAGE (507) (first added in the v0.9.20 release)\n```\n\n[Back to TOC](#nginx-api-for-lua)\n\nNginx log level constants\n-------------------------\n\n**context:** *init_by_lua&#42;, init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\n```lua\n\n   ngx.STDERR\n   ngx.EMERG\n   ngx.ALERT\n   ngx.CRIT\n   ngx.ERR\n   ngx.WARN\n   ngx.NOTICE\n   ngx.INFO\n   ngx.DEBUG\n```\n\nThese constants are usually used by the [ngx.log](#ngxlog) method.\n\n[Back to TOC](#nginx-api-for-lua)\n\nprint\n-----\n\n**syntax:** *print(...)*\n\n**context:** *init_by_lua&#42;, init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nWrites argument values into the Nginx `error.log` file with the `ngx.NOTICE` log level.\n\nIt is equivalent to\n\n```lua\n\n ngx.log(ngx.NOTICE, ...)\n```\n\nLua `nil` arguments are accepted and result in literal `\"nil\"` strings while Lua booleans result in literal `\"true\"` or `\"false\"` strings. And the `ngx.null` constant will yield the `\"null\"` string output.\n\nThere is a hard coded `2048` byte limitation on error message lengths in the Nginx core. This limit includes trailing newlines and leading time stamps. If the message size exceeds this limit, Nginx will truncate the message text accordingly. This limit can be manually modified by editing the `NGX_MAX_ERROR_STR` macro definition in the `src/core/ngx_log.h` file in the Nginx source tree.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.ctx\n-------\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, exit_worker_by_lua&#42;*\n\nThis table can be used to store per-request Lua context data and has a life time identical to the current request (as with the Nginx variables).\n\nConsider the following example,\n\n```nginx\n\n location /test {\n     rewrite_by_lua_block {\n         ngx.ctx.foo = 76\n     }\n     access_by_lua_block {\n         ngx.ctx.foo = ngx.ctx.foo + 3\n     }\n     content_by_lua_block {\n         ngx.say(ngx.ctx.foo)\n     }\n }\n```\n\nThen `GET /test` will yield the output\n\n```bash\n\n 79\n```\n\nThat is, the `ngx.ctx.foo` entry persists across the rewrite, access, and content phases of a request.\n\nEvery request, including subrequests, has its own copy of the table. For example:\n\n```nginx\n\n location /sub {\n     content_by_lua_block {\n         ngx.say(\"sub pre: \", ngx.ctx.blah)\n         ngx.ctx.blah = 32\n         ngx.say(\"sub post: \", ngx.ctx.blah)\n     }\n }\n\n location /main {\n     content_by_lua_block {\n         ngx.ctx.blah = 73\n         ngx.say(\"main pre: \", ngx.ctx.blah)\n         local res = ngx.location.capture(\"/sub\")\n         ngx.print(res.body)\n         ngx.say(\"main post: \", ngx.ctx.blah)\n     }\n }\n```\n\nThen `GET /main` will give the output\n\n```bash\n\n main pre: 73\n sub pre: nil\n sub post: 32\n main post: 73\n```\n\nHere, 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`.\n\nInternal 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,\n\n```nginx\n\n location /new {\n     content_by_lua_block {\n         ngx.say(ngx.ctx.foo)\n     }\n }\n\n location /orig {\n     content_by_lua_block {\n         ngx.ctx.foo = \"hello\"\n         ngx.exec(\"/new\")\n     }\n }\n```\n\nThen `GET /orig` will give\n\n```bash\n\n nil\n```\n\nrather than the original `\"hello\"` value.\n\nBecause HTTP request is created after SSL handshake, the `ngx.ctx` created\nin [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)\nis not available in the following phases like [rewrite_by_lua*](#rewrite_by_lua).\n\nSince `v0.10.18`, the `ngx.ctx` created during a SSL handshake\nwill be inherited by the requests which share the same TCP connection established by the handshake.\nNote that overwrite values in `ngx.ctx` in the http request phases (like `rewrite_by_lua*`) will only take affect in the current http request.\n\nArbitrary data values, including Lua closures and nested tables, can be inserted into this \"magic\" table. It also allows the registration of custom meta methods.\n\nOverriding `ngx.ctx` with a new Lua table is also supported, for example,\n\n```lua\n\n ngx.ctx = { foo = 32, bar = 54 }\n```\n\nWhen being used in the context of [init_worker_by_lua*](#init_worker_by_lua), this table just has the same lifetime of the current Lua handler.\n\nThe `ngx.ctx` lookup requires relatively expensive metamethod calls and it is much slower than explicitly passing per-request data along by your own function arguments. So do not abuse this API for saving your own function arguments because it usually has quite some performance impact.\n\nBecause of the metamethod magic, never \"local\" the `ngx.ctx` table outside your Lua function scope on the Lua module level due to [worker-level data sharing](#data-sharing-within-an-nginx-worker). For example, the following is bad:\n\n```lua\n\n -- mymodule.lua\n local _M = {}\n\n -- the following line is bad since ngx.ctx is a per-request\n -- data while this <code>ctx</code> variable is on the Lua module level\n -- and thus is per-nginx-worker.\n local ctx = ngx.ctx\n\n function _M.main()\n     ctx.foo = \"bar\"\n end\n\n return _M\n```\n\nUse the following instead:\n\n```lua\n\n -- mymodule.lua\n local _M = {}\n\n function _M.main(ctx)\n     ctx.foo = \"bar\"\n end\n\n return _M\n```\n\nThat is, let the caller pass the `ctx` table explicitly via a function argument.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.location.capture\n--------------------\n\n**syntax:** *res = ngx.location.capture(uri, options?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nIssues a synchronous but still non-blocking *Nginx Subrequest* using `uri`.\n\nNginx's subrequests provide a powerful way to make non-blocking internal requests to other locations configured with disk file directory or *any* other Nginx C modules like `ngx_proxy`, `ngx_fastcgi`, `ngx_memc`,\n`ngx_postgres`, `ngx_drizzle`, and even ngx_lua itself and etc etc etc.\n\nAlso note that subrequests just mimic the HTTP interface but there is *no* extra HTTP/TCP traffic *nor* IPC involved. Everything works internally, efficiently, on the C level.\n\nSubrequests are completely different from HTTP 301/302 redirection (via [ngx.redirect](#ngxredirect)) and internal redirection (via [ngx.exec](#ngxexec)).\n\nYou should always read the request body (by either calling [ngx.req.read_body](#ngxreqread_body) or configuring [lua_need_request_body](#lua_need_request_body) on) before initiating a subrequest.\n\nThis API function (as well as [ngx.location.capture_multi](#ngxlocationcapture_multi)) always buffers the whole response body of the subrequest in memory. Thus, you should use [cosockets](#ngxsockettcp)\nand streaming processing instead if you have to handle large subrequest responses.\n\nHere is a basic example:\n\n```lua\n\n res = ngx.location.capture(uri)\n```\n\nReturns a Lua table with 4 slots: `res.status`, `res.header`, `res.body`, and `res.truncated`.\n\n`res.status` holds the response status code for the subrequest response.\n\n`res.header` holds all the response headers of the\nsubrequest and it is a normal Lua table. For multi-value response headers,\nthe value is a Lua (array) table that holds all the values in the order that\nthey appear. For instance, if the subrequest response headers contain the following\nlines:\n\n```bash\n\n Set-Cookie: a=3\n Set-Cookie: foo=bar\n Set-Cookie: baz=blah\n```\n\nThen `res.header[\"Set-Cookie\"]` will be evaluated to the table value\n`{\"a=3\", \"foo=bar\", \"baz=blah\"}`.\n\n`res.body` holds the subrequest's response body data, which might be truncated. You always need to check the `res.truncated` boolean flag to see if `res.body` contains truncated data. The data truncation here can only be caused by those unrecoverable errors in your subrequests like the cases that the remote end aborts the connection prematurely in the middle of the response body data stream or a read timeout happens when your subrequest is receiving the response body data from the remote.\n\nURI query strings can be concatenated to URI itself, for instance,\n\n```lua\n\n res = ngx.location.capture('/foo/bar?a=3&b=4')\n```\n\nNamed locations like `@foo` are not allowed due to a limitation in\nthe Nginx core. Use normal locations combined with the `internal` directive to\nprepare internal-only locations.\n\nAn optional option table can be fed as the second\nargument, which supports the options:\n\n* `method`\n\tspecify the subrequest's request method, which only accepts constants like `ngx.HTTP_POST`.\n* `body`\n\tspecify the subrequest's request body (string value only).\n* `args`\n\tspecify the subrequest's URI query arguments (both string value and Lua tables are accepted)\n* `headers`\n    specify the subrequest's request headers (Lua table only). this headers will override the original headers of the subrequest.\n* `ctx`\n\tspecify 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.\n* `vars`\n\ttake a Lua table which holds the values to set the specified Nginx variables in the subrequest as this option's value. This option was first introduced in the `v0.3.1rc31` release.\n* `copy_all_vars`\n\tspecify whether to copy over all the Nginx variable values of the current request to the subrequest in question. modifications of the Nginx variables in the subrequest will not affect the current (parent) request. This option was first introduced in the `v0.3.1rc31` release.\n* `share_all_vars`\n\tspecify whether to share all the Nginx variables of the subrequest with the current (parent) request. modifications of the Nginx variables in the subrequest will affect the current (parent) request. Enabling this option may lead to hard-to-debug issues due to bad side-effects and is considered bad and harmful. Only enable this option when you completely know what you are doing.\n* `always_forward_body`\n\twhen set to true, the current (parent) request's request body will always be forwarded to the subrequest being created if the `body` option is not specified. The request body read by either [ngx.req.read_body()](#ngxreqread_body) or [lua_need_request_body on](#lua_need_request_body) will be directly forwarded to the subrequest without copying the whole request body data when creating the subrequest (no matter the request body data is buffered in memory buffers or temporary files). By default, this option is `false` and when the `body` option is not specified, the request body of the current (parent) request is only forwarded when the subrequest takes the `PUT` or `POST` request method.\n\nIssuing a POST subrequest, for example, can be done as follows\n\n```lua\n\n res = ngx.location.capture(\n     '/foo/bar',\n     { method = ngx.HTTP_POST, body = 'hello, world' }\n )\n```\n\nSee HTTP method constants methods other than POST.\nThe `method` option is `ngx.HTTP_GET` by default.\n\nThe `args` option can specify extra URI arguments, for instance,\n\n```lua\n\n ngx.location.capture('/foo?a=1',\n     { args = { b = 3, c = ':' } }\n )\n```\n\nis equivalent to\n\n```lua\n\n ngx.location.capture('/foo?a=1&b=3&c=%3a')\n```\n\nthat is, this method will escape argument keys and values according to URI rules and\nconcatenate them together into a complete query string. The format for the Lua table passed as the `args` argument is identical to the format used in the [ngx.encode_args](#ngxencode_args) method.\n\nThe `args` option can also take plain query strings:\n\n```lua\n\n ngx.location.capture('/foo?a=1',\n     { args = 'b=3&c=%3a' }\n )\n```\n\nThis is functionally identical to the previous examples.\n\nThe `share_all_vars` option controls whether to share Nginx variables among the current request and its subrequests.\nIf this option is set to `true`, then the current request and associated subrequests will share the same Nginx variable scope. Hence, changes to Nginx variables made by a subrequest will affect the current request.\n\nCare should be taken in using this option as variable scope sharing can have unexpected side effects. The `args`, `vars`, or `copy_all_vars` options are generally preferable instead.\n\nThis option is set to `false` by default\n\n```nginx\n\n location /other {\n     set $dog \"$dog world\";\n     echo \"$uri dog: $dog\";\n }\n\n location /lua {\n     set $dog 'hello';\n     content_by_lua_block {\n         res = ngx.location.capture(\"/other\",\n             { share_all_vars = true })\n\n         ngx.print(res.body)\n         ngx.say(ngx.var.uri, \": \", ngx.var.dog)\n     }\n }\n```\n\nAccessing location `/lua` gives\n\n\n    /other dog: hello world\n    /lua: hello world\n\n\nThe `copy_all_vars` option provides a copy of the parent request's Nginx variables to subrequests when such subrequests are issued. Changes made to these variables by such subrequests will not affect the parent request or any other subrequests sharing the parent request's variables.\n\n```nginx\n\n location /other {\n     set $dog \"$dog world\";\n     echo \"$uri dog: $dog\";\n }\n\n location /lua {\n     set $dog 'hello';\n     content_by_lua_block {\n         res = ngx.location.capture(\"/other\",\n             { copy_all_vars = true })\n\n         ngx.print(res.body)\n         ngx.say(ngx.var.uri, \": \", ngx.var.dog)\n     }\n }\n```\n\nRequest `GET /lua` will give the output\n\n\n    /other dog: hello world\n    /lua: hello\n\n\nNote that if both `share_all_vars` and `copy_all_vars` are set to true, then `share_all_vars` takes precedence.\n\nIn addition to the two settings above, it is possible to specify\nvalues for variables in the subrequest using the `vars` option. These\nvariables are set after the sharing or copying of variables has been\nevaluated, and provides a more efficient method of passing specific\nvalues to a subrequest over encoding them as URL arguments and\nunescaping them in the Nginx config file.\n\n```nginx\n\n location /other {\n     content_by_lua_block {\n         ngx.say(\"dog = \", ngx.var.dog)\n         ngx.say(\"cat = \", ngx.var.cat)\n     }\n }\n\n location /lua {\n     set $dog '';\n     set $cat '';\n     content_by_lua_block {\n         res = ngx.location.capture(\"/other\",\n             { vars = { dog = \"hello\", cat = 32 }})\n\n         ngx.print(res.body)\n     }\n }\n```\n\nAccessing `/lua` will yield the output\n\n\n    dog = hello\n    cat = 32\n\nThe `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,\n\n```lua\n\nlocation /foo {\n    content_by_lua_block {\n        ngx.print(ngx.var.http_x_test)\n    }\n}\n\nlocation /lua {\n    content_by_lua_block {\n        local res = ngx.location.capture(\"/foo\", {\n            headers = {\n                [\"X-Test\"] = \"aa\",\n            }\n        })\n        ngx.print(res.body)\n    }\n}\n```\n\nAccessing `/lua` will yield the output\n\n\n    aa\n\n\nThe `ctx` option can be used to specify a custom Lua table to serve as the [ngx.ctx](#ngxctx) table for the subrequest.\n\n```nginx\n\n location /sub {\n     content_by_lua_block {\n         ngx.ctx.foo = \"bar\";\n     }\n }\n location /lua {\n     content_by_lua_block {\n         local ctx = {}\n         res = ngx.location.capture(\"/sub\", { ctx = ctx })\n\n         ngx.say(ctx.foo)\n         ngx.say(ngx.ctx.foo)\n     }\n }\n```\n\nThen request `GET /lua` gives\n\n\n    bar\n    nil\n\n\nIt is also possible to use this `ctx` option to share the same [ngx.ctx](#ngxctx) table between the current (parent) request and the subrequest:\n\n```nginx\n\n location /sub {\n     content_by_lua_block {\n         ngx.ctx.foo = \"bar\"\n     }\n }\n location /lua {\n     content_by_lua_block {\n         res = ngx.location.capture(\"/sub\", { ctx = ngx.ctx })\n         ngx.say(ngx.ctx.foo)\n     }\n }\n```\n\nRequest `GET /lua` yields the output\n\n\n    bar\n\n\nNote that subrequests issued by [ngx.location.capture](#ngxlocationcapture) inherit all the\nrequest headers of the current request by default and that this may have unexpected side effects on the\nsubrequest responses. For example, when using the standard `ngx_proxy` module to serve\nsubrequests, an \"Accept-Encoding: gzip\" header in the main request may result\nin gzipped responses that cannot be handled properly in Lua code. Original request headers should be ignored by setting\n[proxy_pass_request_headers](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass_request_headers) to `off` in subrequest locations.\n\nWhen the `body` option is not specified and the `always_forward_body` option is false (the default value), the `POST` and `PUT` subrequests will inherit the request bodies of the parent request (if any).\n\nThere is a hard-coded upper limit on the number of subrequests possible for every main request. In older versions of Nginx, the limit was `50` concurrent subrequests and in more recent versions, Nginx `1.9.5` onwards, the same limit is changed to limit the depth of recursive subrequests. When this limit is exceeded, the following error message is added to the `error.log` file:\n\n\n    [error] 13983#0: *1 subrequests cycle while processing \"/uri\"\n\n\nThe limit can be manually modified if required by editing the definition of the `NGX_HTTP_MAX_SUBREQUESTS` macro in the `nginx/src/http/ngx_http_request.h` file in the Nginx source tree.\n\nPlease also refer to restrictions on capturing locations configured by [subrequest directives of other modules](#locations-configured-by-subrequest-directives-of-other-modules).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.location.capture_multi\n--------------------------\n\n**syntax:** *res1, res2, ... = ngx.location.capture_multi({ {uri, options?}, {uri, options?}, ... })*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nJust like [ngx.location.capture](#ngxlocationcapture), but supports multiple subrequests running in parallel.\n\nThis function issues several parallel subrequests specified by the input table and returns their results in the same order. For example,\n\n```lua\n\n res1, res2, res3 = ngx.location.capture_multi{\n     { \"/foo\", { args = \"a=3&b=4\" } },\n     { \"/bar\" },\n     { \"/baz\", { method = ngx.HTTP_POST, body = \"hello\" } },\n }\n\n if res1.status == ngx.HTTP_OK then\n     ...\n end\n\n if res2.body == \"BLAH\" then\n     ...\n end\n```\n\nThis function will not return until all the subrequests terminate.\nThe total latency is the longest latency of the individual subrequests rather than the sum.\n\nLua tables can be used for both requests and responses when the number of subrequests to be issued is not known in advance:\n\n```lua\n\n -- construct the requests table\n local reqs = {}\n table.insert(reqs, { \"/mysql\" })\n table.insert(reqs, { \"/postgres\" })\n table.insert(reqs, { \"/redis\" })\n table.insert(reqs, { \"/memcached\" })\n\n -- issue all the requests at once and wait until they all return\n local resps = {\n     ngx.location.capture_multi(reqs)\n }\n\n -- loop over the responses table\n for i, resp in ipairs(resps) do\n     -- process the response table \"resp\"\n end\n```\n\nThe [ngx.location.capture](#ngxlocationcapture) function is just a special form\nof this function. Logically speaking, the [ngx.location.capture](#ngxlocationcapture) can be implemented like this\n\n```lua\n\n ngx.location.capture =\n     function (uri, args)\n         return ngx.location.capture_multi({ {uri, args} })\n     end\n```\n\nPlease also refer to restrictions on capturing locations configured by [subrequest directives of other modules](#locations-configured-by-subrequest-directives-of-other-modules).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.status\n----------\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;*\n\nRead and write the current request's response status. This should be called\nbefore sending out the response headers.\n\n```lua\n\n ngx.status = ngx.HTTP_CREATED\n status = ngx.status\n```\n\nSetting `ngx.status` after the response header is sent out has no effect but leaving an error message in your Nginx's error log file:\n\n\n    attempt to set ngx.status after sending out response headers\n\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.header.HEADER\n-----------------\n\n**syntax:** *ngx.header.HEADER = VALUE*\n\n**syntax:** *value = ngx.header.HEADER*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;*\n\nSet, add to, or clear the current request's `HEADER` response header that is to be sent.\n\nUnderscores (`_`) in the header names will be replaced by hyphens (`-`) by default. This transformation can be turned off via the [lua_transform_underscores_in_response_headers](#lua_transform_underscores_in_response_headers) directive.\n\nThe header names are matched case-insensitively.\n\n```lua\n\n -- equivalent to ngx.header[\"Content-Type\"] = 'text/plain'\n ngx.header.content_type = 'text/plain'\n\n ngx.header[\"X-My-Header\"] = 'blah blah'\n```\n\nMulti-value headers can be set this way:\n\n```lua\n\n ngx.header['Set-Cookie'] = {'a=32; path=/', 'b=4; path=/'}\n```\n\nwill yield\n\n```bash\n\n Set-Cookie: a=32; path=/\n Set-Cookie: b=4; path=/\n```\n\nin the response headers.\n\nOnly Lua tables are accepted (Only the last element in the table will take effect for standard headers such as `Content-Type` that only accept a single value).\n\n```lua\n\n ngx.header.content_type = {'a', 'b'}\n```\n\nis equivalent to\n\n```lua\n\n ngx.header.content_type = 'b'\n```\n\nSetting a slot to `nil` effectively removes it from the response headers:\n\n```lua\n\n ngx.header[\"X-My-Header\"] = nil\n```\n\nThe same applies to assigning an empty table:\n\n```lua\n\n ngx.header[\"X-My-Header\"] = {}\n```\n\nSetting `ngx.header.HEADER` after sending out response headers (either explicitly with [ngx.send_headers](#ngxsend_headers) or implicitly with [ngx.print](#ngxprint) and similar) will log an error message.\n\nReading `ngx.header.HEADER` will return the value of the response header named `HEADER`.\n\nUnderscores (`_`) in the header names will also be replaced by dashes (`-`) and the header names will be matched case-insensitively. If the response header is not present at all, `nil` will be returned.\n\nThis is particularly useful in the context of [header_filter_by_lua*](#header_filter_by_lua), for example,\n\n```nginx\n\n location /test {\n     set $footer '';\n\n     proxy_pass http://some-backend;\n\n     header_filter_by_lua_block {\n         if ngx.header[\"X-My-Header\"] == \"blah\" then\n             ngx.var.footer = \"some value\"\n         end\n     }\n\n     echo_after_body $footer;\n }\n```\n\nFor multi-value headers, all of the values of header will be collected in order and returned as a Lua table. For example, response headers\n\n\n    Foo: bar\n    Foo: baz\n\n\nwill result in\n\n```lua\n\n {\"bar\", \"baz\"}\n```\n\nto be returned when reading `ngx.header.Foo`.\n\nNote that `ngx.header` is not a normal Lua table and as such, it is not possible to iterate through it using the Lua `ipairs` function.\n\nNote: this function throws a Lua error if `HEADER` or\n`VALUE` contain unsafe characters (control characters).\n\nFor reading *request* headers, use the [ngx.req.get_headers](#ngxreqget_headers) function instead.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.resp.get_headers\n--------------------\n\n**syntax:** *headers, err = ngx.resp.get_headers(max_headers?, raw?)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, balancer_by_lua&#42;*\n\nReturns a Lua table holding all the current response headers for the current request.\n\n```lua\n\n local h, err = ngx.resp.get_headers()\n\n if err == \"truncated\" then\n     -- one can choose to ignore or reject the current response here\n end\n\n for k, v in pairs(h) do\n     ...\n end\n```\n\nThis function has the same signature as [ngx.req.get_headers](#ngxreqget_headers) except getting response headers instead of request headers.\n\nNote that a maximum of 100 response headers are parsed by default (including those with the same name) and that additional response headers are silently discarded to guard against potential denial of service attacks. Since `v0.10.13`, when the limit is exceeded, it will return a second value which is the string `\"truncated\"`.\n\nThis API was first introduced in the `v0.9.5` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.is_internal\n-------------------\n\n**syntax:** *is_internal = ngx.req.is_internal()*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;*\n\nReturns a boolean indicating whether the current request is an \"internal request\", i.e.,\na request initiated from inside the current Nginx server instead of from the client side.\n\nSubrequests are all internal requests and so are requests after internal redirects.\n\nThis API was first introduced in the `v0.9.20` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.start_time\n------------------\n\n**syntax:** *secs = ngx.req.start_time()*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;*\n\nReturns a floating-point number representing the timestamp (including milliseconds as the decimal part) when the current request was created.\n\nThe following example emulates the `$request_time` variable value (provided by [ngx_http_log_module](http://nginx.org/en/docs/http/ngx_http_log_module.html)) in pure Lua:\n\n```lua\n\n local request_time = ngx.now() - ngx.req.start_time()\n```\n\nThis function was first introduced in the `v0.7.7` release.\n\nSee also [ngx.now](#ngxnow) and [ngx.update_time](#ngxupdate_time).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.http_version\n--------------------\n\n**syntax:** *num = ngx.req.http_version()*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;*\n\nReturns the HTTP version number for the current request as a Lua number.\n\nCurrent possible values are 3.0, 2.0, 1.0, 1.1, and 0.9. Returns `nil` for unrecognized values.\n\nThis method was first introduced in the `v0.7.17` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.raw_header\n------------------\n\n**syntax:** *str = ngx.req.raw_header(no_request_line?)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;*\n\nReturns the original raw HTTP protocol header received by the Nginx server.\n\nBy default, the request line and trailing `CR LF` terminator will also be included. For example,\n\n```lua\n\n ngx.print(ngx.req.raw_header())\n```\n\ngives something like this:\n\n\n    GET /t HTTP/1.1\n    Host: localhost\n    Connection: close\n    Foo: bar\n\n\n\nYou can specify the optional\n`no_request_line` argument as a `true` value to exclude the request line from the result. For example,\n\n```lua\n\n ngx.print(ngx.req.raw_header(true))\n```\n\noutputs something like this:\n\n\n    Host: localhost\n    Connection: close\n    Foo: bar\n\n\n\nThis method was first introduced in the `v0.7.17` release.\n\nThis method does not work in HTTP/2 requests yet.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.get_method\n------------------\n\n**syntax:** *method_name = ngx.req.get_method()*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, balancer_by_lua&#42;, log_by_lua&#42;*\n\nRetrieves the current request's request method name. Strings like `\"GET\"` and `\"POST\"` are returned instead of numerical [method constants](#http-method-constants).\n\nIf the current request is an Nginx subrequest, then the subrequest's method name will be returned.\n\nThis method was first introduced in the `v0.5.6` release.\n\nSee also [ngx.req.set_method](#ngxreqset_method).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.set_method\n------------------\n\n**syntax:** *ngx.req.set_method(method_id)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;*\n\nOverrides the current request's request method with the `method_id` argument. Currently only numerical [method constants](#http-method-constants) are supported, like `ngx.HTTP_POST` and `ngx.HTTP_GET`.\n\nIf the current request is an Nginx subrequest, then the subrequest's method will be overridden.\n\nThis method was first introduced in the `v0.5.6` release.\n\nSee also [ngx.req.get_method](#ngxreqget_method).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.set_uri\n---------------\n\n**syntax:** *ngx.req.set_uri(uri, jump?, binary?)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;*\n\nRewrite the current request's (parsed) URI by the `uri` argument. The `uri` argument must be a Lua string and cannot be of zero length, or a Lua exception will be thrown.\n\nThe optional boolean `jump` argument can trigger location rematch (or location jump) as [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, that is, when `jump` is `true` (default to `false`), this function will never return and it will tell Nginx to try re-searching locations with the new URI value at the later `post-rewrite` phase and jumping to the new location.\n\nLocation jump will not be triggered otherwise, and only the current request's URI will be modified, which is also the default behavior. This function will return but with no returned values when the `jump` argument is `false` or absent altogether.\n\nFor example, the following Nginx config snippet\n\n```nginx\n\n rewrite ^ /foo last;\n```\n\ncan be coded in Lua like this:\n\n```lua\n\n ngx.req.set_uri(\"/foo\", true)\n```\n\nSimilarly, Nginx config\n\n```nginx\n\n rewrite ^ /foo break;\n```\n\ncan be coded in Lua as\n\n```lua\n\n ngx.req.set_uri(\"/foo\", false)\n```\n\nor equivalently,\n\n```lua\n\n ngx.req.set_uri(\"/foo\")\n```\n\nThe `jump` argument can only be set to `true` in [rewrite_by_lua*](#rewrite_by_lua). Use of jump in other contexts is prohibited and will throw out a Lua exception.\n\nA more sophisticated example involving regex substitutions is as follows\n\n```nginx\n\n location /test {\n     rewrite_by_lua_block {\n         local uri = ngx.re.sub(ngx.var.uri, \"^/test/(.*)\", \"/$1\", \"o\")\n         ngx.req.set_uri(uri)\n     }\n     proxy_pass http://my_backend;\n }\n```\n\nwhich is functionally equivalent to\n\n```nginx\n\n location /test {\n     rewrite ^/test/(.*) /$1 break;\n     proxy_pass http://my_backend;\n }\n```\n\nNote: this function throws a Lua error if the `uri` argument\ncontains unsafe characters (control characters).\n\nNote that it is not possible to use this interface to rewrite URI arguments and that [ngx.req.set_uri_args](#ngxreqset_uri_args) should be used for this instead. For instance, Nginx config\n\n```nginx\n\n rewrite ^ /foo?a=3? last;\n```\n\ncan be coded as\n\n```nginx\n\n ngx.req.set_uri_args(\"a=3\")\n ngx.req.set_uri(\"/foo\", true)\n```\n\nor\n\n```nginx\n\n ngx.req.set_uri_args({a = 3})\n ngx.req.set_uri(\"/foo\", true)\n```\n\nStarting from `0.10.16` of this module, this function accepts an\noptional boolean `binary` argument to allow arbitrary binary URI\ndata. By default, this `binary` argument is false and this function\nwill throw out a Lua error such as the one below when the `uri`\nargument contains any control characters (ASCII Code 0 ~ 0x08, 0x0A ~ 0x1F and 0x7F).\n\n\n    [error] 23430#23430: *1 lua entry thread aborted: runtime error:\n    content_by_lua(nginx.conf:44):3: ngx.req.set_uri unsafe byte \"0x00\"\n    in \"\\x00foo\" (maybe you want to set the 'binary' argument?)\n\n\nThis interface was first introduced in the `v0.3.1rc14` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.set_uri_args\n--------------------\n\n**syntax:** *ngx.req.set_uri_args(args)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;*\n\nRewrite the current request's URI query arguments by the `args` argument. The `args` argument can be either a Lua string, as in\n\n```lua\n\n ngx.req.set_uri_args(\"a=3&b=hello%20world\")\n```\n\nor a Lua table holding the query arguments' key-value pairs, as in\n\n```lua\n\n ngx.req.set_uri_args({ a = 3, b = \"hello world\" })\n```\n\nIn the former case, i.e., when the whole query-string is provided directly,\nthe input Lua string should already be well-formed with the URI encoding.\nFor security considerations, this method will automatically escape any control and\nwhitespace characters (ASCII code 0x00 ~ 0x20 and 0x7F) in the Lua string.\n\nIn the latter case, this method will escape argument keys and values according to the URI escaping rule.\n\nMulti-value arguments are also supported:\n\n```lua\n\n ngx.req.set_uri_args({ a = 3, b = {5, 6} })\n```\n\nwhich will result in a query string like `a=3&b=5&b=6` or `b=5&b=6&a=3`.\n\n**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.**\n\nThis interface was first introduced in the `v0.3.1rc13` release.\n\nSee also [ngx.req.set_uri](#ngxreqset_uri).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.get_uri_args\n--------------------\n\n**syntax:** *args, err = ngx.req.get_uri_args(max_args?, tab?)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, balancer_by_lua&#42;*\n\nReturns a Lua table holding all the current request URL query arguments. An optional `tab` argument\ncan be used to reuse the table returned by this method.\n\n```nginx\n\n location = /test {\n     content_by_lua_block {\n         local args, err = ngx.req.get_uri_args()\n\n         if err == \"truncated\" then\n             -- one can choose to ignore or reject the current request here\n         end\n\n         for key, val in pairs(args) do\n             if type(val) == \"table\" then\n                 ngx.say(key, \": \", table.concat(val, \", \"))\n             else\n                 ngx.say(key, \": \", val)\n             end\n         end\n     }\n }\n```\n\nThen `GET /test?foo=bar&bar=baz&bar=blah` will yield the response body\n\n```bash\n\n foo: bar\n bar: baz, blah\n```\n\nMultiple occurrences of an argument key will result in a table value holding all the values for that key in order.\n\nKeys and values are unescaped according to URI escaping rules. In the settings above, `GET /test?a%20b=1%61+2` will yield:\n\n```bash\n\n a b: 1a 2\n```\n\nArguments without the `=<value>` parts are treated as boolean arguments. `GET /test?foo&bar` will yield:\n\n```bash\n\n foo: true\n bar: true\n```\n\nThat is, they will take Lua boolean values `true`. However, they are different from arguments taking empty string values. `GET /test?foo=&bar=` will give something like\n\n```bash\n\n foo:\n bar:\n```\n\nEmpty key arguments are discarded. `GET /test?=hello&=world` will yield an empty output for instance.\n\nUpdating query arguments via the Nginx variable `$args` (or `ngx.var.args` in Lua) at runtime is also supported:\n\n```lua\n\n ngx.var.args = \"a=3&b=42\"\n local args, err = ngx.req.get_uri_args()\n```\n\nHere the `args` table will always look like\n\n```lua\n\n {a = 3, b = 42}\n```\n\nregardless of the actual request query string.\n\nNote that a maximum of 100 request arguments are parsed by default (including those with the same name) and that additional request arguments are silently discarded to guard against potential denial of service attacks. Since `v0.10.13`, when the limit is exceeded, it will return a second value which is the string `\"truncated\"`.\n\nHowever, the optional `max_args` function argument can be used to override this limit:\n\n```lua\n\n local args, err = ngx.req.get_uri_args(10)\n if err == \"truncated\" then\n     -- one can choose to ignore or reject the current request here\n end\n```\n\nThis argument can be set to zero to remove the limit and to process all request arguments received:\n\n```lua\n\n local args, err = ngx.req.get_uri_args(0)\n```\n\nRemoving the `max_args` cap is strongly discouraged.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.get_post_args\n---------------------\n\n**syntax:** *args, err = ngx.req.get_post_args(max_args?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;*\n\nReturns a Lua table holding all the current request POST query arguments (of the MIME type `application/x-www-form-urlencoded`). Call [ngx.req.read_body](#ngxreqread_body) to read the request body first or turn on the [lua_need_request_body](#lua_need_request_body) directive to avoid errors.\n\n```nginx\n\n location = /test {\n     content_by_lua_block {\n         ngx.req.read_body()\n         local args, err = ngx.req.get_post_args()\n\n         if err == \"truncated\" then\n             -- one can choose to ignore or reject the current request here\n         end\n\n         if not args then\n             ngx.say(\"failed to get post args: \", err)\n             return\n         end\n         for key, val in pairs(args) do\n             if type(val) == \"table\" then\n                 ngx.say(key, \": \", table.concat(val, \", \"))\n             else\n                 ngx.say(key, \": \", val)\n             end\n         end\n     }\n }\n```\n\nThen\n\n```bash\n\n # Post request with the body 'foo=bar&bar=baz&bar=blah'\n $ curl --data 'foo=bar&bar=baz&bar=blah' localhost/test\n```\n\nwill yield the response body like\n\n```bash\n\n foo: bar\n bar: baz, blah\n```\n\nMultiple occurrences of an argument key will result in a table value holding all of the values for that key in order.\n\nKeys and values will be unescaped according to URI escaping rules.\n\nWith the settings above,\n\n```bash\n\n # POST request with body 'a%20b=1%61+2'\n $ curl -d 'a%20b=1%61+2' localhost/test\n```\n\nwill yield:\n\n```bash\n\n a b: 1a 2\n```\n\nArguments without the `=<value>` parts are treated as boolean arguments. `POST /test` with the request body `foo&bar` will yield:\n\n```bash\n\n foo: true\n bar: true\n```\n\nThat is, they will take Lua boolean values `true`. However, they are different from arguments taking empty string values. `POST /test` with request body `foo=&bar=` will return something like\n\n```bash\n\n foo:\n bar:\n```\n\nEmpty key arguments are discarded. `POST /test` with body `=hello&=world` will yield empty outputs for instance.\n\nNote that a maximum of 100 request arguments are parsed by default (including those with the same name) and that additional request arguments are silently discarded to guard against potential denial of service attacks. Since `v0.10.13`, when the limit is exceeded, it will return a second value which is the string `\"truncated\"`.\n\nHowever, the optional `max_args` function argument can be used to override this limit:\n\n```lua\n\n local args, err = ngx.req.get_post_args(10)\n if err == \"truncated\" then\n     -- one can choose to ignore or reject the current request here\n end\n```\n\nThis argument can be set to zero to remove the limit and to process all request arguments received:\n\n```lua\n\n local args, err = ngx.req.get_post_args(0)\n```\n\nRemoving the `max_args` cap is strongly discouraged.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.get_headers\n-------------------\n\n**syntax:** *headers, err = ngx.req.get_headers(max_headers?, raw?)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;*\n\nReturns a Lua table holding all the current request headers.\n\n```lua\n\n local h, err = ngx.req.get_headers()\n\n if err == \"truncated\" then\n     -- one can choose to ignore or reject the current request here\n end\n\n for k, v in pairs(h) do\n     ...\n end\n```\n\nTo read an individual header:\n\n```lua\n\n ngx.say(\"Host: \", ngx.req.get_headers()[\"Host\"])\n```\n\nNote that the [ngx.var.HEADER](#ngxvarvariable) API call, which uses core [$http_HEADER](http://nginx.org/en/docs/http/ngx_http_core_module.html#var_http_) variables, may be more preferable for reading individual request headers.\n\nFor multiple instances of request headers such as:\n\n```bash\n\n Foo: foo\n Foo: bar\n Foo: baz\n```\n\nthe value of `ngx.req.get_headers()[\"Foo\"]` will be a Lua (array) table such as:\n\n```lua\n\n {\"foo\", \"bar\", \"baz\"}\n```\n\nNote that a maximum of 100 request headers are parsed by default (including those with the same name) and that additional request headers are silently discarded to guard against potential denial of service attacks. Since `v0.10.13`, when the limit is exceeded, it will return a second value which is the string `\"truncated\"`.\n\nHowever, the optional `max_headers` function argument can be used to override this limit:\n\n```lua\n\n local headers, err = ngx.req.get_headers(10)\n\n if err == \"truncated\" then\n     -- one can choose to ignore or reject the current request here\n end\n```\n\nThis argument can be set to zero to remove the limit and to process all request headers received:\n\n```lua\n\n local headers, err = ngx.req.get_headers(0)\n```\n\nRemoving the `max_headers` cap is strongly discouraged.\n\nSince the `0.6.9` release, all the header names in the Lua table returned are converted to the pure lower-case form by default, unless the `raw` argument is set to `true` (default to `false`).\n\nAlso, by default, an `__index` metamethod is added to the resulting Lua table and will normalize the keys to a pure lowercase form with all underscores converted to dashes in case of a lookup miss. For example, if a request header `My-Foo-Header` is present, then the following invocations will all pick up the value of this header correctly:\n\n```lua\n\n ngx.say(headers.my_foo_header)\n ngx.say(headers[\"My-Foo-Header\"])\n ngx.say(headers[\"my-foo-header\"])\n```\n\nThe `__index` metamethod will not be added when the `raw` argument is set to `true`.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.set_header\n------------------\n\n**syntax:** *ngx.req.set_header(header_name, header_value)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;*\n\nSet the current request's request header named `header_name` to value `header_value`, overriding any existing ones.\n\nThe input Lua string `header_name` and `header_value` should already be well-formed with the URI encoding.\nFor security considerations, this method will automatically escape \" \", \"\"\", \"(\", \")\", \",\", \"/\", \":\", \";\", \"?\",\n\"<\", \"=\", \">\", \"?\", \"@\", \"[\", \"]\", \"\\\", \"{\", \"}\", 0x00-0x1F, 0x7F-0xFF in `header_name` and automatically escape\n\"0x00-0x08, 0x0A-0x0F, 0x7F in `header_value`.\n\nBy default, all the subrequests subsequently initiated by [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi) will inherit the new header.\n\nIt 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.\n\nHere is an example of setting the `Content-Type` header:\n\n```lua\n\n ngx.req.set_header(\"Content-Type\", \"text/css\")\n```\n\nThe `header_value` can take an array list of values,\nfor example,\n\n```lua\n\n ngx.req.set_header(\"Foo\", {\"a\", \"abc\"})\n```\n\nwill produce two new request headers:\n\n```bash\n\n Foo: a\n Foo: abc\n```\n\nand old `Foo` headers will be overridden if there is any.\n\nWhen the `header_value` argument is `nil`, the request header will be removed. So\n\n```lua\n\n ngx.req.set_header(\"X-Foo\", nil)\n```\n\nis equivalent to\n\n```lua\n\n ngx.req.clear_header(\"X-Foo\")\n```\n\nNote: this function throws a Lua error if `header_name` or\n`header_value` contain unsafe characters (control characters).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.clear_header\n--------------------\n\n**syntax:** *ngx.req.clear_header(header_name)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;*\n\nClears the current request's request header named `header_name`. None of the current request's existing subrequests will be affected but subsequently initiated subrequests will inherit the change by default.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.read_body\n-----------------\n\n**syntax:** *ngx.req.read_body()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nReads the client request body synchronously without blocking the Nginx event loop.\n\n```lua\n\n ngx.req.read_body()\n local args = ngx.req.get_post_args()\n```\n\nIf the request body is already read previously by turning on [lua_need_request_body](#lua_need_request_body) or by using other modules, then this function does not run and returns immediately.\n\nIf the request body has already been explicitly discarded, either by the [ngx.req.discard_body](#ngxreqdiscard_body) function or other modules, this function does not run and returns immediately.\n\nIn case of errors, such as connection errors while reading the data, this method will throw out a Lua exception *or* terminate the current request with a 500 status code immediately.\n\nThe request body data read using this function can be retrieved later via [ngx.req.get_body_data](#ngxreqget_body_data) or, alternatively, the temporary file name for the body data cached to disk using [ngx.req.get_body_file](#ngxreqget_body_file). This depends on\n\n1. whether the current request body is already larger than the [client_body_buffer_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size),\n1. and whether [client_body_in_file_only](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_in_file_only) has been switched on.\n\nIn cases where current request may have a request body and the request body data is not required, The [ngx.req.discard_body](#ngxreqdiscard_body) function must be used to explicitly discard the request body to avoid breaking things under HTTP 1.1 keepalive or HTTP 1.1 pipelining.\n\nThis function was first introduced in the `v0.3.1rc17` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.discard_body\n--------------------\n\n**syntax:** *ngx.req.discard_body()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nExplicitly discard the request body, i.e., read the data on the connection and throw it away immediately (without using the request body by any means).\n\nThis function is an asynchronous call and returns immediately.\n\nIf the request body has already been read, this function does nothing and returns immediately.\n\nThis function was first introduced in the `v0.3.1rc17` release.\n\nSee also [ngx.req.read_body](#ngxreqread_body).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.get_body_data\n---------------------\n\n**syntax:** *data = ngx.req.get_body_data(max_bytes?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, log_by_lua&#42;*\n\nRetrieves 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.\n\nThe optional `max_bytes` argument can be used when you don't need the entire body.\n\nThis function returns `nil` if\n\n1. the request body has not been read,\n1. the request body has been read into disk temporary files,\n1. or the request body has zero size.\n\nIf the request body has not been read yet, call [ngx.req.read_body](#ngxreqread_body) first (or turn on [lua_need_request_body](#lua_need_request_body) to force this module to read the request body. This is not recommended however).\n\nIf the request body has been read into disk files, try calling the [ngx.req.get_body_file](#ngxreqget_body_file) function instead.\n\nTo force in-memory request bodies, try setting [client_body_buffer_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size) to the same size value in [client_max_body_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size).\n\nNote that calling this function instead of using `ngx.var.request_body` or `ngx.var.echo_request_body` is more efficient because it can save one dynamic memory allocation and one data copy.\n\nThis function was first introduced in the `v0.3.1rc17` release.\n\nSee also [ngx.req.get_body_file](#ngxreqget_body_file).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.get_body_file\n---------------------\n\n**syntax:** *file_name = ngx.req.get_body_file()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nRetrieves the file name for the in-file request body data. Returns `nil` if the request body has not been read or has been read into memory.\n\nThe returned file is read only and is usually cleaned up by Nginx's memory pool. It should not be manually modified, renamed, or removed in Lua code.\n\nIf the request body has not been read yet, call [ngx.req.read_body](#ngxreqread_body) first (or turn on [lua_need_request_body](#lua_need_request_body) to force this module to read the request body. This is not recommended however).\n\nIf the request body has been read into memory, try calling the [ngx.req.get_body_data](#ngxreqget_body_data) function instead.\n\nTo 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).\n\nNote 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.\n\nThis function was first introduced in the `v0.3.1rc17` release.\n\nSee also [ngx.req.get_body_data](#ngxreqget_body_data).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.set_body_data\n---------------------\n\n**syntax:** *ngx.req.set_body_data(data)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, balancer_by_lua&#42;,*\n\nSet the current request's request body using the in-memory data specified by the `data` argument.\n\nIf the request body has not been read yet, call [ngx.req.read_body](#ngxreqread_body) first (or turn on [lua_need_request_body](#lua_need_request_body) to force this module to read the request body. This is not recommended however). Additionally, the request body must not have been previously discarded by [ngx.req.discard_body](#ngxreqdiscard_body).\n\nWhether 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.\n\nNote 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.\n\nThis function was first introduced in the `v0.3.1rc18` release.\n\nSee also [ngx.req.set_body_file](#ngxreqset_body_file).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.set_body_file\n---------------------\n\n**syntax:** *ngx.req.set_body_file(file_name, auto_clean?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, balancer_by_lua&#42;,*\n\nSet the current request's request body using the in-file data specified by the `file_name` argument.\n\nIf the request body has not been read yet, call [ngx.req.read_body](#ngxreqread_body) first (or turn on [lua_need_request_body](#lua_need_request_body) to force this module to read the request body. This is not recommended however). Additionally, the request body must not have been previously discarded by [ngx.req.discard_body](#ngxreqdiscard_body).\n\nIf the optional `auto_clean` argument is given a `true` value, then this file will be removed at request completion or the next time this function or [ngx.req.set_body_data](#ngxreqset_body_data) are called in the same request. The `auto_clean` is default to `false`.\n\nPlease ensure that the file specified by the `file_name` argument exists and is readable by an Nginx worker process by setting its permission properly to avoid Lua exception errors.\n\nWhether 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.\n\nThis function was first introduced in the `v0.3.1rc18` release.\n\nSee also [ngx.req.set_body_data](#ngxreqset_body_data).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.init_body\n-----------------\n\n**syntax:** *ngx.req.init_body(buffer_size?)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nCreates 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.\n\nIf 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.\n\nWhen the data can no longer be hold in the memory buffer for the request body, then the data will be flushed onto a temporary file just like the standard request body reader in the Nginx core.\n\nIt is important to always call the [ngx.req.finish_body](#ngxreqfinish_body) after all the data has been appended onto the current request body. Also, when this function is used together with [ngx.req.socket](#ngxreqsocket), it is required to call [ngx.req.socket](#ngxreqsocket) *before* this function, or you will get the \"request body already exists\" error message.\n\nThe usage of this function is often like this:\n\n```lua\n\n ngx.req.init_body(128 * 1024)  -- buffer is 128KB\n for chunk in next_data_chunk() do\n     ngx.req.append_body(chunk) -- each chunk can be 4KB\n end\n ngx.req.finish_body()\n```\n\nThis function can be used with [ngx.req.append_body](#ngxreqappend_body), [ngx.req.finish_body](#ngxreqfinish_body), and [ngx.req.socket](#ngxreqsocket) to implement efficient input filters in pure Lua (in the context of [rewrite_by_lua*](#rewrite_by_lua) or [access_by_lua*](#access_by_lua)), which can be used with other Nginx content handler or upstream modules like [ngx_http_proxy_module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html) and [ngx_http_fastcgi_module](http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html).\n\nThis function was first introduced in the `v0.5.11` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.append_body\n-------------------\n\n**syntax:** *ngx.req.append_body(data_chunk)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nAppend new data chunk specified by the `data_chunk` argument onto the existing request body created by the [ngx.req.init_body](#ngxreqinit_body) call.\n\nWhen the data can no longer be hold in the memory buffer for the request body, then the data will be flushed onto a temporary file just like the standard request body reader in the Nginx core.\n\nIt is important to always call the [ngx.req.finish_body](#ngxreqfinish_body) after all the data has been appended onto the current request body.\n\nThis function can be used with [ngx.req.init_body](#ngxreqinit_body), [ngx.req.finish_body](#ngxreqfinish_body), and [ngx.req.socket](#ngxreqsocket) to implement efficient input filters in pure Lua (in the context of [rewrite_by_lua*](#rewrite_by_lua) or [access_by_lua*](#access_by_lua)), which can be used with other Nginx content handler or upstream modules like [ngx_http_proxy_module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html) and [ngx_http_fastcgi_module](http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html).\n\nThis function was first introduced in the `v0.5.11` release.\n\nSee also [ngx.req.init_body](#ngxreqinit_body).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.finish_body\n-------------------\n\n**syntax:** *ngx.req.finish_body()*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nCompletes the construction process of the new request body created by the [ngx.req.init_body](#ngxreqinit_body) and [ngx.req.append_body](#ngxreqappend_body) calls.\n\nThis function can be used with [ngx.req.init_body](#ngxreqinit_body), [ngx.req.append_body](#ngxreqappend_body), and [ngx.req.socket](#ngxreqsocket) to implement efficient input filters in pure Lua (in the context of [rewrite_by_lua*](#rewrite_by_lua) or [access_by_lua*](#access_by_lua)), which can be used with other Nginx content handler or upstream modules like [ngx_http_proxy_module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html) and [ngx_http_fastcgi_module](http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html).\n\nThis function was first introduced in the `v0.5.11` release.\n\nSee also [ngx.req.init_body](#ngxreqinit_body).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.req.socket\n--------------\n\n**syntax:** *tcpsock, err = ngx.req.socket()*\n\n**syntax:** *tcpsock, err = ngx.req.socket(raw)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nReturns a read-only cosocket object that wraps the downstream connection. Only [receive](#tcpsockreceive), [receiveany](#tcpsockreceiveany) and [receiveuntil](#tcpsockreceiveuntil) methods are supported on this object.\n\nIn case of error, `nil` will be returned as well as a string describing the error.\n\n**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.\n\nThe 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).\n\nIf 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.\nChunked request bodies are not yet supported in this API.\n\nSince the `v0.9.0` release, this function accepts an optional boolean `raw` argument. When this argument is `true`, this function returns a full-duplex cosocket object wrapping around the raw downstream connection socket, upon which you can call the [receive](#tcpsockreceive), [receiveany](#tcpsockreceiveany), [receiveuntil](#tcpsockreceiveuntil), and [send](#tcpsocksend) methods.\n\nWhen the `raw` argument is `true`, it is required that no pending data from any previous [ngx.say](#ngxsay), [ngx.print](#ngxprint), or [ngx.send_headers](#ngxsend_headers) calls exists. So if you have these downstream output calls previously, you should call [ngx.flush(true)](#ngxflush) before calling `ngx.req.socket(true)` to ensure that there is no pending output data. If the request body has not been read yet, then this \"raw socket\" can also be used to read the request body.\n\nYou can use the \"raw request socket\" returned by `ngx.req.socket(true)` to implement fancy protocols like [WebSocket](https://en.wikipedia.org/wiki/WebSocket), or just emit your own raw HTTP response header or body data. You can refer to the [lua-resty-websocket library](https://github.com/openresty/lua-resty-websocket) for a real world example.\n\nThis function was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.exec\n--------\n\n**syntax:** *ngx.exec(uri, args?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nDoes an internal redirect to `uri` with `args` and is similar to the [echo_exec](http://github.com/openresty/echo-nginx-module#echo_exec) directive of the [echo-nginx-module](http://github.com/openresty/echo-nginx-module).\n\n```lua\n\n ngx.exec('/some-location')\n ngx.exec('/some-location', 'a=3&b=5&c=6')\n ngx.exec('/some-location?a=3&b=5', 'c=6')\n```\n\nThe optional second `args` can be used to specify extra URI query arguments, for example:\n\n```lua\n\n ngx.exec(\"/foo\", \"a=3&b=hello%20world\")\n```\n\nAlternatively, a Lua table can be passed for the `args` argument for ngx_lua to carry out URI escaping and string concatenation.\n\n```lua\n\n ngx.exec(\"/foo\", { a = 3, b = \"hello world\" })\n```\n\nThe result is exactly the same as the previous example.\n\nThe format for the Lua table passed as the `args` argument is identical to the format used in the [ngx.encode_args](#ngxencode_args) method.\n\nNamed locations are also supported but the second `args` argument will be ignored if present and the querystring for the new target is inherited from the referring location (if any).\n\n`GET /foo/file.php?a=hello` will return \"hello\" and not \"goodbye\" in the example below\n\n```nginx\n\n location /foo {\n     content_by_lua_block {\n         ngx.exec(\"@bar\", \"a=goodbye\")\n     }\n }\n\n location @bar {\n     content_by_lua_block {\n         local args = ngx.req.get_uri_args()\n         for key, val in pairs(args) do\n             if key == \"a\" then\n                 ngx.say(val)\n             end\n         end\n     }\n }\n```\n\nNote that the `ngx.exec` method is different from [ngx.redirect](#ngxredirect) in that\nit is purely an internal redirect and that no new external HTTP traffic is involved.\n\nAlso note that this method call terminates the processing of the current request and that it *must* be called before [ngx.send_headers](#ngxsend_headers) or explicit response body\noutputs by either [ngx.print](#ngxprint) or [ngx.say](#ngxsay).\n\nIt is recommended that a coding style that combines this method call with the `return` statement, i.e., `return ngx.exec(...)` be adopted when this method call is used in contexts other than [header_filter_by_lua*](#header_filter_by_lua) to reinforce the fact that the request processing is being terminated.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.redirect\n------------\n\n**syntax:** *ngx.redirect(uri, status?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nIssue an `HTTP 301` or `302` redirection to `uri`.\n\nNote: this function throws a Lua error if the `uri` argument\ncontains unsafe characters (control characters).\n\nThe optional `status` parameter specifies the HTTP status code to be used. The following status codes are supported right now:\n\n* `301`\n* `302` (default)\n* `303`\n* `307`\n* `308`\n\nIt is `302` (`ngx.HTTP_MOVED_TEMPORARILY`) by default.\n\nHere is an example assuming the current server name is `localhost` and that it is listening on port 1984:\n\n```lua\n\n return ngx.redirect(\"/foo\")\n```\n\nwhich is equivalent to\n\n```lua\n\n return ngx.redirect(\"/foo\", ngx.HTTP_MOVED_TEMPORARILY)\n```\n\nRedirecting arbitrary external URLs is also supported, for example:\n\n```lua\n\n return ngx.redirect(\"http://www.google.com\")\n```\n\nWe can also use the numerical code directly as the second `status` argument:\n\n```lua\n\n return ngx.redirect(\"/foo\", 301)\n```\n\nThis method is similar to the [rewrite](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite) directive with the `redirect` modifier in the standard\n[ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html), for example, this `nginx.conf` snippet\n\n```nginx\n\n rewrite ^ /foo? redirect;  # nginx config\n```\n\nis equivalent to the following Lua code\n\n```lua\n\n return ngx.redirect('/foo')  -- Lua code\n```\n\nwhile\n\n```nginx\n\n rewrite ^ /foo? permanent;  # nginx config\n```\n\nis equivalent to\n\n```lua\n\n return ngx.redirect('/foo', ngx.HTTP_MOVED_PERMANENTLY)  -- Lua code\n```\n\nURI arguments can be specified as well, for example:\n\n```lua\n\n return ngx.redirect('/foo?a=3&b=4')\n```\n\nNote that this method call terminates the processing of the current request and that it *must* be called before [ngx.send_headers](#ngxsend_headers) or explicit response body\noutputs by either [ngx.print](#ngxprint) or [ngx.say](#ngxsay).\n\nIt is recommended that a coding style that combines this method call with the `return` statement, i.e., `return ngx.redirect(...)` be adopted when this method call is used in contexts other than [header_filter_by_lua*](#header_filter_by_lua) to reinforce the fact that the request processing is being terminated.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.send_headers\n----------------\n\n**syntax:** *ok, err = ngx.send_headers()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nExplicitly send out the response headers.\n\nSince `v0.8.3` this function returns `1` on success, or returns `nil` and a string describing the error otherwise.\n\nNote that there is normally no need to manually send out response headers as ngx_lua will automatically send headers out\nbefore content is output with [ngx.say](#ngxsay) or [ngx.print](#ngxprint) or when [content_by_lua*](#content_by_lua) exits normally.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.headers_sent\n----------------\n\n**syntax:** *value = ngx.headers_sent*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nReturns `true` if the response headers have been sent (by ngx_lua), and `false` otherwise.\n\nThis API was first introduced in ngx_lua v0.3.1rc6.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.print\n---------\n\n**syntax:** *ok, err = ngx.print(...)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nEmits arguments concatenated to the HTTP client (as response body). If response headers have not been sent, this function will send headers out first and then output body data.\n\nSince `v0.8.3` this function returns `1` on success, or returns `nil` and a string describing the error otherwise.\n\nLua `nil` values will output `\"nil\"` strings and Lua boolean values will output `\"true\"` and `\"false\"` literal strings respectively.\n\nNested arrays of strings are permitted and the elements in the arrays will be sent one by one:\n\n```lua\n\n local table = {\n     \"hello, \",\n     {\"world: \", true, \" or \", false,\n         {\": \", nil}}\n }\n ngx.print(table)\n```\n\nwill yield the output\n\n```bash\n\n hello, world: true or false: nil\n```\n\nNon-array table arguments will cause a Lua exception to be thrown.\n\nThe `ngx.null` constant will yield the `\"null\"` string output.\n\nThis is an asynchronous call and will return immediately without waiting for all the data to be written into the system send buffer. To run in synchronous mode, call `ngx.flush(true)` after calling `ngx.print`. This can be particularly useful for streaming output. See [ngx.flush](#ngxflush) for more details.\n\nPlease note that both `ngx.print` and [ngx.say](#ngxsay) will always invoke the whole Nginx output body filter chain, which is an expensive operation. So be careful when calling either of these two in a tight loop; buffer the data yourself in Lua and save the calls.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.say\n-------\n\n**syntax:** *ok, err = ngx.say(...)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nJust as [ngx.print](#ngxprint) but also emit a trailing newline.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.log\n-------\n\n**syntax:** *ngx.log(log_level, ...)*\n\n**context:** *init_by_lua&#42;, init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nLog arguments concatenated to error.log with the given logging level.\n\nLua `nil` arguments are accepted and result in literal `\"nil\"` string while Lua booleans result in literal `\"true\"` or `\"false\"` string outputs. And the `ngx.null` constant will yield the `\"null\"` string output.\n\nThe `log_level` argument can take constants like `ngx.ERR` and `ngx.WARN`. Check out [Nginx log level constants](#nginx-log-level-constants) for details.\n\nThere is a hard coded `2048` byte limitation on error message lengths in the Nginx core. This limit includes trailing newlines and leading time stamps. If the message size exceeds this limit, Nginx will truncate the message text accordingly. This limit can be manually modified by editing the `NGX_MAX_ERROR_STR` macro definition in the `src/core/ngx_log.h` file in the Nginx source tree.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.flush\n---------\n\n**syntax:** *ok, err = ngx.flush(wait?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nFlushes response output to the client.\n\n`ngx.flush` accepts an optional boolean `wait` argument (Default: `false`) first introduced in the `v0.3.1rc34` release. When called with the default argument, it issues an asynchronous call (Returns immediately without waiting for output data to be written into the system send buffer). Calling the function with the `wait` argument set to `true` switches to synchronous mode.\n\nIn synchronous mode, the function will not return until all output data has been written into the system send buffer or until the [send_timeout](http://nginx.org/en/docs/http/ngx_http_core_module.html#send_timeout) setting has expired. Note that using the Lua coroutine mechanism means that this function does not block the Nginx event loop even in the synchronous mode.\n\nWhen `ngx.flush(true)` is called immediately after [ngx.print](#ngxprint) or [ngx.say](#ngxsay), it causes the latter functions to run in synchronous mode. This can be particularly useful for streaming output.\n\nNote that `ngx.flush` is not functional when in the HTTP 1.0 output buffering mode. See [HTTP 1.0 support](#http-10-support).\n\nSince `v0.8.3` this function returns `1` on success, or returns `nil` and a string describing the error otherwise.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.exit\n--------\n\n**syntax:** *ngx.exit(status)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nWhen `status >= 200` (i.e., `ngx.HTTP_OK` and above), it will interrupt the execution of the current request and return status code to Nginx.\n\nWhen `status == 0` (i.e., `ngx.OK`), it will only quit the current phase handler (or the content handler if the [content_by_lua*](#content_by_lua) directive is used) and continue to run later phases (if any) for the current request.\n\nThe `status` argument can be `ngx.OK`, `ngx.ERROR`, `ngx.HTTP_NOT_FOUND`,\n`ngx.HTTP_MOVED_TEMPORARILY`, or other [HTTP status constants](#http-status-constants).\n\nTo return an error page with custom contents, use code snippets like this:\n\n```lua\n\n ngx.status = ngx.HTTP_GONE\n ngx.say(\"This is our own content\")\n -- to cause quit the whole request rather than the current phase handler\n ngx.exit(ngx.HTTP_OK)\n```\n\nThe effect in action:\n\n```bash\n\n $ curl -i http://localhost/test\n HTTP/1.1 410 Gone\n Server: nginx/1.0.6\n Date: Thu, 15 Sep 2011 00:51:48 GMT\n Content-Type: text/plain\n Transfer-Encoding: chunked\n Connection: keep-alive\n\n This is our own content\n```\n\nNumber literals can be used directly as the argument, for instance,\n\n```lua\n\n ngx.exit(501)\n```\n\nNote that while this method accepts all [HTTP status constants](#http-status-constants) as input, it only accepts `ngx.OK` and `ngx.ERROR` of the [core constants](#core-constants).\n\nAlso note that this method call terminates the processing of the current request and that it is recommended that a coding style that combines this method call with the `return` statement, i.e., `return ngx.exit(...)` be used to reinforce the fact that the request processing is being terminated.\n\nWhen being used in the contexts of [header_filter_by_lua*](#header_filter_by_lua), [balancer_by_lua*](#balancer_by_lua_block), and\n[ssl_session_store_by_lua*](#ssl_session_store_by_lua_block), `ngx.exit()` is\nan asynchronous operation and will return immediately. This behavior may change in future and it is recommended that users always use `return` in combination as suggested above.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.eof\n-------\n\n**syntax:** *ok, err = ngx.eof()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nExplicitly specify the end of the response output stream. In the case of HTTP 1.1 chunked encoded output, it will just trigger the Nginx core to send out the \"last chunk\".\n\nWhen you disable the HTTP 1.1 keep-alive feature for your downstream connections, you can rely on well written HTTP clients to close the connection actively for you when you call this method. This trick can be used do back-ground jobs without letting the HTTP clients to wait on the connection, as in the following example:\n\n```nginx\n\n location = /async {\n     keepalive_timeout 0;\n     content_by_lua_block {\n         ngx.say(\"got the task!\")\n         ngx.eof()  -- well written HTTP clients will close the connection at this point\n         -- access MySQL, PostgreSQL, Redis, Memcached, and etc here...\n     }\n }\n```\n\nBut if you create subrequests to access other locations configured by Nginx upstream modules, then you should configure those upstream modules to ignore client connection abortions if they are not by default. For example, by default the standard [ngx_http_proxy_module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html) will terminate both the subrequest and the main request as soon as the client closes the connection, so it is important to turn on the [proxy_ignore_client_abort](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ignore_client_abort) directive in your location block configured by [ngx_http_proxy_module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html):\n\n```nginx\n\n proxy_ignore_client_abort on;\n```\n\nA better way to do background jobs is to use the [ngx.timer.at](#ngxtimerat) API.\n\nSince `v0.8.3` this function returns `1` on success, or returns `nil` and a string describing the error otherwise.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.sleep\n---------\n\n**syntax:** *ngx.sleep(seconds)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSleeps for the specified seconds without blocking. One can specify time resolution up to 0.001 seconds (i.e., one millisecond).\n\nBehind the scene, this method makes use of the Nginx timers.\n\nSince the `0.7.20` release, The `0` time argument can also be specified.\n\nThis method was introduced in the `0.5.0rc30` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.escape_uri\n--------------\n\n**syntax:** *newstr = ngx.escape_uri(str, type?)*\n\n**context:** *init_by_lua&#42;, init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSince `v0.10.16`, this function accepts an optional `type` argument.\nIt accepts the following values (defaults to `2`):\n\n* `0`: escapes `str` as a full URI. And the characters\n` ` (space), `#`, `%`,\n`?`, 0x00 ~ 0x1F, 0x7F ~ 0xFF will be escaped.\n* `2`: escape `str` as a URI component. All characters except\nalphabetic characters, digits, `-`, `.`, `_`,\n`~` will be encoded as `%XX`.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.unescape_uri\n----------------\n\n**syntax:** *newstr = ngx.unescape_uri(str)*\n\n**context:** *init_by_lua&#42;, init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nUnescape `str` as an escaped URI component.\n\nFor example,\n\n```lua\n\n ngx.say(ngx.unescape_uri(\"b%20r56+7\"))\n```\n\ngives the output\n\n\n    b r56 7\n\n\nInvalid 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.\n\nFor example,\n\n```lua\n\n ngx.say(ngx.unescape_uri(\"try %search%%20%again%\"))\n```\n\ngives the output\n\n\n    try %search% %again%\n\n\n(Note that `%20` following `%` got unescaped, even it can be considered a part of invalid sequence.)\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.encode_args\n---------------\n\n**syntax:** *str = ngx.encode_args(table)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nEncode the Lua table to a query args string according to the URI encoded rules.\n\nFor example,\n\n```lua\n\n ngx.encode_args({foo = 3, [\"b r\"] = \"hello world\"})\n```\n\nyields\n\n\n    foo=3&b%20r=hello%20world\n\n\nThe table keys must be Lua strings.\n\nMulti-value query args are also supported. Just use a Lua table for the argument's value, for example:\n\n```lua\n\n ngx.encode_args({baz = {32, \"hello\"}})\n```\n\ngives\n\n\n    baz=32&baz=hello\n\n\nIf the value table is empty and the effect is equivalent to the `nil` value.\n\nBoolean argument values are also supported, for instance,\n\n```lua\n\n ngx.encode_args({a = true, b = 1})\n```\n\nyields\n\n\n    a&b=1\n\n\nIf the argument value is `false`, then the effect is equivalent to the `nil` value.\n\nThis method was first introduced in the `v0.3.1rc27` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.decode_args\n---------------\n\n**syntax:** *table, err = ngx.decode_args(str, max_args?)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nDecodes a URI encoded query-string into a Lua table. This is the inverse function of [ngx.encode_args](#ngxencode_args).\n\nThe optional `max_args` argument can be used to specify the maximum number of arguments parsed from the `str` argument. By default, a maximum of 100 request arguments are parsed (including those with the same name) and that additional URI arguments are silently discarded to guard against potential denial of service attacks. Since `v0.10.13`, when the limit is exceeded, it will return a second value which is the string `\"truncated\"`.\n\nThis argument can be set to zero to remove the limit and to process all request arguments received:\n\n```lua\n\n local args = ngx.decode_args(str, 0)\n```\n\nRemoving the `max_args` cap is strongly discouraged.\n\nThis method was introduced in the `v0.5.0rc29`.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.encode_base64\n-----------------\n\n**syntax:** *newstr = ngx.encode_base64(str, no_padding?)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nEncodes `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).\n\nSince 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).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.decode_base64\n-----------------\n\n**syntax:** *newstr = ngx.decode_base64(str)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nDecodes 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).\n\nThe `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.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.decode_base64mime\n---------------------\n**syntax:** *newstr = ngx.decode_base64mime(str)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;*\n\n**requires:** `resty.core.base64` or `resty.core`\n\nDecodes the `str` argument as a base64 digest to the raw form.\nThe `str` follows base64 transfer encoding for MIME (RFC 2045), and will discard characters outside the base encoding alphabet.\nReturns `nil` if `str` is not well formed.\n\n '''Note:''' This method requires the <code>resty.core.base64</code> or <code>resty.core</code> modules from the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.crc32_short\n---------------\n\n**syntax:** *intval = ngx.crc32_short(str)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nCalculates the CRC-32 (Cyclic Redundancy Code) digest for the `str` argument.\n\nThis method performs better on relatively short `str` inputs (i.e., less than 30 ~ 60 bytes), as compared to [ngx.crc32_long](#ngxcrc32_long). The result is exactly the same as [ngx.crc32_long](#ngxcrc32_long).\n\nBehind the scene, it is just a thin wrapper around the `ngx_crc32_short` function defined in the Nginx core.\n\nThis API was first introduced in the `v0.3.1rc8` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.crc32_long\n--------------\n\n**syntax:** *intval = ngx.crc32_long(str)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nCalculates the CRC-32 (Cyclic Redundancy Code) digest for the `str` argument.\n\nThis method performs better on relatively long `str` inputs (i.e., longer than 30 ~ 60 bytes), as compared to [ngx.crc32_short](#ngxcrc32_short).  The result is exactly the same as [ngx.crc32_short](#ngxcrc32_short).\n\nBehind the scene, it is just a thin wrapper around the `ngx_crc32_long` function defined in the Nginx core.\n\nThis API was first introduced in the `v0.3.1rc8` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.hmac_sha1\n-------------\n\n**syntax:** *digest = ngx.hmac_sha1(secret_key, str)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nComputes the [HMAC-SHA1](https://en.wikipedia.org/wiki/HMAC) digest of the argument `str` and turns the result using the secret key `<secret_key>`.\n\nThe raw binary form of the `HMAC-SHA1` digest will be generated, use [ngx.encode_base64](#ngxencode_base64), for example, to encode the result to a textual representation if desired.\n\nFor example,\n\n```lua\n\n local key = \"thisisverysecretstuff\"\n local src = \"some string we want to sign\"\n local digest = ngx.hmac_sha1(key, src)\n ngx.say(ngx.encode_base64(digest))\n```\n\nyields the output\n\n\n    R/pvxzHC4NLtj7S+kXFg/NePTmk=\n\n\nThis API requires the OpenSSL library enabled in the Nginx build (usually by passing the `--with-http_ssl_module` option to the `./configure` script).\n\nThis function was first introduced in the `v0.3.1rc29` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.md5\n-------\n\n**syntax:** *digest = ngx.md5(str)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns the hexadecimal representation of the MD5 digest of the `str` argument.\n\nFor example,\n\n```nginx\n\n location = /md5 {\n     content_by_lua_block {\n         ngx.say(ngx.md5(\"hello\"))\n     }\n }\n```\n\nyields the output\n\n\n    5d41402abc4b2a76b9719d911017c592\n\n\nSee [ngx.md5_bin](#ngxmd5_bin) if the raw binary MD5 digest is required.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.md5_bin\n-----------\n\n**syntax:** *digest = ngx.md5_bin(str)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns the binary form of the MD5 digest of the `str` argument.\n\nSee [ngx.md5](#ngxmd5) if the hexadecimal form of the MD5 digest is required.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.sha1_bin\n------------\n\n**syntax:** *digest = ngx.sha1_bin(str)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns the binary form of the SHA-1 digest of the `str` argument.\n\nThis function requires SHA-1 support in the Nginx build. (This usually just means OpenSSL should be installed while building Nginx).\n\nThis function was first introduced in the `v0.5.0rc6`.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.quote_sql_str\n-----------------\n\n**syntax:** *quoted_value = ngx.quote_sql_str(raw_value)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns a quoted SQL string literal according to the MySQL quoting rules.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.today\n---------\n\n**syntax:** *str = ngx.today()*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns current date (in the format `yyyy-mm-dd`) from the Nginx cached time (no syscall involved unlike Lua's date library).\n\nThis is the local time.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.time\n--------\n\n**syntax:** *secs = ngx.time()*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns the elapsed seconds from the epoch for the current time stamp from the Nginx cached time (no syscall involved unlike Lua's date library).\n\nUpdates of the Nginx time cache can be forced by calling [ngx.update_time](#ngxupdate_time) first.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.now\n-------\n\n**syntax:** *secs = ngx.now()*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns 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).\n\nYou can forcibly update the Nginx time cache by calling [ngx.update_time](#ngxupdate_time) first.\n\nThis API was first introduced in `v0.3.1rc32`.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.update_time\n---------------\n\n**syntax:** *ngx.update_time()*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nForcibly updates the Nginx current time cache. This call involves a syscall and thus has some overhead, so do not abuse it.\n\nThis API was first introduced in `v0.3.1rc32`.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.localtime\n-------------\n\n**syntax:** *str = ngx.localtime()*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns 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).\n\nThis is the local time.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.utctime\n-----------\n\n**syntax:** *str = ngx.utctime()*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns 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).\n\nThis is the UTC time.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.cookie_time\n---------------\n\n**syntax:** *str = ngx.cookie_time(sec)*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns 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)).\n\n```nginx\n\n ngx.say(ngx.cookie_time(1290079655))\n     -- yields \"Thu, 18-Nov-10 11:27:35 GMT\"\n```\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.http_time\n-------------\n\n**syntax:** *str = ngx.http_time(sec)*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns 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)).\n\n```nginx\n\n ngx.say(ngx.http_time(1290079655))\n     -- yields \"Thu, 18 Nov 2010 11:27:35 GMT\"\n```\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.parse_http_time\n-------------------\n\n**syntax:** *sec = ngx.parse_http_time(str)*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nParse 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.\n\n```nginx\n\n local time = ngx.parse_http_time(\"Thu, 18 Nov 2010 11:27:35 GMT\")\n if time == nil then\n     ...\n end\n```\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.is_subrequest\n-----------------\n\n**syntax:** *value = ngx.is_subrequest*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;*\n\nReturns `true` if the current request is an Nginx subrequest, or `false` otherwise.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.re.match\n------------\n\n**syntax:** *captures, err = ngx.re.match(subject, regex, options?, ctx?, res_table?)*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nMatches the `subject` string using the Perl compatible regular expression `regex` with the optional `options`.\n\nOnly the first occurrence of the match is returned, or `nil` if no match is found. In case of errors, like seeing a bad regular expression or exceeding the PCRE stack limit, `nil` and a string describing the error will be returned.\n\nWhen a match is found, a Lua table `captures` is returned, where `captures[0]` holds the whole substring being matched, and `captures[1]` holds the first parenthesized sub-pattern's capturing, `captures[2]` the second, and so on.\n\n```lua\n\n local m, err = ngx.re.match(\"hello, 1234\", \"[0-9]+\")\n if m then\n     -- m[0] == \"1234\"\n\n else\n     if err then\n         ngx.log(ngx.ERR, \"error: \", err)\n         return\n     end\n\n     ngx.say(\"match not found\")\n end\n```\n\n```lua\n\n local m, err = ngx.re.match(\"hello, 1234\", \"([0-9])[0-9]+\")\n -- m[0] == \"1234\"\n -- m[1] == \"1\"\n```\n\nNamed captures are also supported since the `v0.7.14` release\nand are returned in the same Lua table as key-value pairs as the numbered captures.\n\n```lua\n\n local m, err = ngx.re.match(\"hello, 1234\", \"([0-9])(?<remaining>[0-9]+)\")\n -- m[0] == \"1234\"\n -- m[1] == \"1\"\n -- m[2] == \"234\"\n -- m[\"remaining\"] == \"234\"\n```\n\nUnmatched subpatterns will have `false` values in their `captures` table fields.\n\n```lua\n\n local m, err = ngx.re.match(\"hello, world\", \"(world)|(hello)|(?<named>howdy)\")\n -- m[0] == \"hello\"\n -- m[1] == false\n -- m[2] == \"hello\"\n -- m[3] == false\n -- m[\"named\"] == false\n```\n\nSpecify `options` to control how the match operation will be performed. The following option characters are supported:\n\n\n    a             anchored mode (only match from the beginning)\n\n    d             enable the DFA mode (or the longest token match semantics).\n                  this requires PCRE 6.0+ or else a Lua exception will be thrown.\n                  first introduced in ngx_lua v0.3.1rc30.\n\n    D             enable duplicate named pattern support. This allows named\n                  subpattern names to be repeated, returning the captures in\n                  an array-like Lua table. for example,\n                    local m = ngx.re.match(\"hello, world\",\n                                           \"(?<named>\\w+), (?<named>\\w+)\",\n                                           \"D\")\n                    -- m[\"named\"] == {\"hello\", \"world\"}\n                  this option was first introduced in the v0.7.14 release.\n                  this option requires at least PCRE 8.12.\n\n    i             case insensitive mode (similar to Perl's /i modifier)\n\n    j             enable PCRE JIT compilation, this requires PCRE 8.21+ which\n                  must be built with the --enable-jit option. for optimum performance,\n                  this option should always be used together with the 'o' option.\n                  first introduced in ngx_lua v0.3.1rc30.\n\n    J             enable the PCRE Javascript compatible mode. this option was\n                  first introduced in the v0.7.14 release. this option requires\n                  at least PCRE 8.12.\n\n    m             multi-line mode (similar to Perl's /m modifier)\n\n    o             compile-once mode (similar to Perl's /o modifier),\n                  to enable the worker-process-level compiled-regex cache\n\n    s             single-line mode (similar to Perl's /s modifier)\n\n    u             UTF-8 mode. this requires PCRE to be built with\n                  the --enable-utf8 option or else a Lua exception will be thrown.\n\n    U             similar to \"u\" but disables PCRE's UTF-8 validity check on\n                  the subject string. first introduced in ngx_lua v0.8.1.\n\n    x             extended mode (similar to Perl's /x modifier)\n\n\nThese options can be combined:\n\n```nginx\n\n local m, err = ngx.re.match(\"hello, world\", \"HEL LO\", \"ix\")\n -- m[0] == \"hello\"\n```\n\n```nginx\n\n local m, err = ngx.re.match(\"hello, 美好生活\", \"HELLO, (.{2})\", \"iu\")\n -- m[0] == \"hello, 美好\"\n -- m[1] == \"美好\"\n```\n\nThe `o` option is useful for performance tuning, because the regex pattern in question will only be compiled once, cached in the worker-process level, and shared among all requests in the current Nginx worker process. The upper limit of the regex cache can be tuned via the [lua_regex_cache_max_entries](#lua_regex_cache_max_entries) directive.\n\nThe optional fourth argument, `ctx`, can be a Lua table holding an optional `pos` field. When the `pos` field in the `ctx` table argument is specified, `ngx.re.match` will start matching from that offset (starting from 1). Regardless of the presence of the `pos` field in the `ctx` table, `ngx.re.match` will always set this `pos` field to the position *after* the substring matched by the whole pattern in case of a successful match. When match fails, the `ctx` table will be left intact.\n\n```lua\n\n local ctx = {}\n local m, err = ngx.re.match(\"1234, hello\", \"[0-9]+\", \"\", ctx)\n      -- m[0] = \"1234\"\n      -- ctx.pos == 5\n```\n\n```lua\n\n local ctx = { pos = 2 }\n local m, err = ngx.re.match(\"1234, hello\", \"[0-9]+\", \"\", ctx)\n      -- m[0] = \"234\"\n      -- ctx.pos == 5\n```\n\nThe `ctx` table argument combined with the `a` regex modifier can be used to construct a lexer atop `ngx.re.match`.\n\nNote that, the `options` argument is not optional when the `ctx` argument is specified and that the empty Lua string (`\"\"`) must be used as placeholder for `options` if no meaningful regex options are required.\n\nThis method requires the PCRE library enabled in Nginx ([Known Issue With Special Escaping Sequences](#special-escaping-sequences)).\n\nTo confirm that PCRE JIT is enabled, activate the Nginx debug log by adding the `--with-debug` option to Nginx or OpenResty's `./configure` script. Then, enable the \"debug\" error log level in `error_log` directive. The following message will be generated if PCRE JIT is enabled:\n\n\n    pcre JIT compiling result: 1\n\n\nStarting from the `0.9.4` release, this function also accepts a 5th argument, `res_table`, for letting the caller supply the Lua table used to hold all the capturing results. Starting from `0.9.6`, it is the caller's responsibility to ensure this table is empty. This is very useful for recycling Lua tables and saving GC and table allocation overhead.\n\nThis feature was introduced in the `v0.2.1rc11` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.re.find\n-----------\n\n**syntax:** *from, to, err = ngx.re.find(subject, regex, options?, ctx?, nth?)*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSimilar 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.\n\nIn case of errors (like bad regexes or any PCRE runtime errors), this API function returns two `nil` values followed by a string describing the error.\n\nIf no match is found, this function just returns a `nil` value.\n\nBelow is an example:\n\n```lua\n\n local s = \"hello, 1234\"\n local from, to, err = ngx.re.find(s, \"([0-9]+)\", \"jo\")\n if from then\n     ngx.say(\"from: \", from)\n     ngx.say(\"to: \", to)\n     ngx.say(\"matched: \", string.sub(s, from, to))\n else\n     if err then\n         ngx.say(\"error: \", err)\n         return\n     end\n     ngx.say(\"not matched!\")\n end\n```\n\nThis example produces the output\n\n    from: 8\n    to: 11\n    matched: 1234\n\nBecause this API function does not create new Lua strings nor new Lua tables, it is much faster than [ngx.re.match](#ngxrematch). It should be used wherever possible.\n\nSince the `0.9.3` release, an optional 5th argument, `nth`, is supported to specify which (submatch) capture's indexes to return. When `nth` is 0 (which is the default), the indexes for the whole matched substring is returned; when `nth` is 1, then the 1st submatch capture's indexes are returned; when `nth` is 2, then the 2nd submatch capture is returned, and so on. When the specified submatch does not have a match, then two `nil` values will be returned. Below is an example for this:\n\n```lua\n\n local str = \"hello, 1234\"\n local from, to = ngx.re.find(str, \"([0-9])([0-9]+)\", \"jo\", nil, 2)\n if from then\n     ngx.say(\"matched 2nd submatch: \", string.sub(str, from, to))  -- yields \"234\"\n end\n```\n\nThis API function was first introduced in the `v0.9.2` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.re.gmatch\n-------------\n\n**syntax:** *iterator, err = ngx.re.gmatch(subject, regex, options?)*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSimilar to [ngx.re.match](#ngxrematch), but returns a Lua iterator instead, so as to let the user programmer iterate all the matches over the `<subject>` string argument with the PCRE `regex`.\n\nIn case of errors, like seeing an ill-formed regular expression, `nil` and a string describing the error will be returned.\n\nHere is a small example to demonstrate its basic usage:\n\n```lua\n\n local iterator, err = ngx.re.gmatch(\"hello, world!\", \"([a-z]+)\", \"i\")\n if not iterator then\n     ngx.log(ngx.ERR, \"error: \", err)\n     return\n end\n\n local m\n m, err = iterator()    -- m[0] == m[1] == \"hello\"\n if err then\n     ngx.log(ngx.ERR, \"error: \", err)\n     return\n end\n\n m, err = iterator()    -- m[0] == m[1] == \"world\"\n if err then\n     ngx.log(ngx.ERR, \"error: \", err)\n     return\n end\n\n m, err = iterator()    -- m == nil\n if err then\n     ngx.log(ngx.ERR, \"error: \", err)\n     return\n end\n```\n\nMore often we just put it into a Lua loop:\n\n```lua\n\n local it, err = ngx.re.gmatch(\"hello, world!\", \"([a-z]+)\", \"i\")\n if not it then\n     ngx.log(ngx.ERR, \"error: \", err)\n     return\n end\n\n while true do\n     local m, err = it()\n     if err then\n         ngx.log(ngx.ERR, \"error: \", err)\n         return\n     end\n\n     if not m then\n         -- no match found (any more)\n         break\n     end\n\n     -- found a match\n     ngx.say(m[0])\n     ngx.say(m[1])\n end\n```\n\nThe optional `options` argument takes exactly the same semantics as the [ngx.re.match](#ngxrematch) method.\n\nThe current implementation requires that the iterator returned should only be used in a single request. That is, one should *not* assign it to a variable belonging to persistent namespace like a Lua package.\n\nThis method requires the PCRE library enabled in Nginx ([Known Issue With Special Escaping Sequences](#special-escaping-sequences)).\n\nThis feature was first introduced in the `v0.2.1rc12` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.re.sub\n----------\n\n**syntax:** *newstr, n, err = ngx.re.sub(subject, regex, replace, options?)*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSubstitutes 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).\n\nThis method returns the resulting new string as well as the number of successful substitutions. In case of failures, like syntax errors in the regular expressions or the `<replace>` string argument, it will return `nil` and a string describing the error.\n\nWhen the `replace` is a string, then it is treated as a special template for string replacement. For example,\n\n```lua\n\n local newstr, n, err = ngx.re.sub(\"hello, 1234\", \"([0-9])[0-9]\", \"[$0][$1]\")\n if not newstr then\n     ngx.log(ngx.ERR, \"error: \", err)\n     return\n end\n\n -- newstr == \"hello, [12][1]34\"\n -- n == 1\n```\n\nwhere `$0` referring to the whole substring matched by the pattern and `$1` referring to the first parenthesized capturing substring.\n\nCurly braces can also be used to disambiguate variable names from the background string literals:\n\n```lua\n\n local newstr, n, err = ngx.re.sub(\"hello, 1234\", \"[0-9]\", \"${0}00\")\n -- newstr == \"hello, 100234\"\n -- n == 1\n```\n\nLiteral dollar sign characters (`$`) in the `replace` string argument can be escaped by another dollar sign, for instance,\n\n```lua\n\n local newstr, n, err = ngx.re.sub(\"hello, 1234\", \"[0-9]\", \"$$\")\n -- newstr == \"hello, $234\"\n -- n == 1\n```\n\nDo not use backlashes to escape dollar signs; it will not work as expected.\n\nWhen the `replace` argument is of type \"function\", then it will be invoked with the \"match table\" as the argument to generate the replace string literal for substitution. The \"match table\" fed into the `replace` function is exactly the same as the return value of [ngx.re.match](#ngxrematch). Here is an example:\n\n```lua\n\n local func = function (m)\n     return \"[\" .. m[0] .. \"][\" .. m[1] .. \"]\"\n end\n\n local newstr, n, err = ngx.re.sub(\"hello, 1234\", \"( [0-9] ) [0-9]\", func, \"x\")\n -- newstr == \"hello, [12][1]34\"\n -- n == 1\n```\n\nThe dollar sign characters in the return value of the `replace` function argument are not special at all.\n\nThis method requires the PCRE library enabled in Nginx ([Known Issue With Special Escaping Sequences](#special-escaping-sequences)).\n\nThis feature was first introduced in the `v0.2.1rc13` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.re.gsub\n-----------\n\n**syntax:** *newstr, n, err = ngx.re.gsub(subject, regex, replace, options?)*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nJust like [ngx.re.sub](#ngxresub), but does global substitution.\n\nHere is some examples:\n\n```lua\n\n local newstr, n, err = ngx.re.gsub(\"hello, world\", \"([a-z])[a-z]+\", \"[$0,$1]\", \"i\")\n if not newstr then\n     ngx.log(ngx.ERR, \"error: \", err)\n     return\n end\n\n -- newstr == \"[hello,h], [world,w]\"\n -- n == 2\n```\n\n```lua\n\n local func = function (m)\n     return \"[\" .. m[0] .. \",\" .. m[1] .. \"]\"\n end\n local newstr, n, err = ngx.re.gsub(\"hello, world\", \"([a-z])[a-z]+\", func, \"i\")\n -- newstr == \"[hello,h], [world,w]\"\n -- n == 2\n```\n\nThis method requires the PCRE library enabled in Nginx ([Known Issue With Special Escaping Sequences](#special-escaping-sequences)).\n\nThis feature was first introduced in the `v0.2.1rc15` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT\n---------------\n\n**syntax:** *dict = ngx.shared.DICT*\n\n**syntax:** *dict = ngx.shared\\[name_var\\]*\n\n**context:** *init_by_lua&#42;, init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nFetching the shm-based Lua dictionary object for the shared memory zone named `DICT` defined by the [lua_shared_dict](#lua_shared_dict) directive.\n\nShared memory zones are always shared by all the Nginx worker processes in the current Nginx server instance.\n\nThe resulting object `dict` has the following methods:\n\n* [get](#ngxshareddictget)\n* [get_stale](#ngxshareddictget_stale)\n* [set](#ngxshareddictset)\n* [safe_set](#ngxshareddictsafe_set)\n* [add](#ngxshareddictadd)\n* [safe_add](#ngxshareddictsafe_add)\n* [replace](#ngxshareddictreplace)\n* [delete](#ngxshareddictdelete)\n* [incr](#ngxshareddictincr)\n* [lpush](#ngxshareddictlpush)\n* [rpush](#ngxshareddictrpush)\n* [lpop](#ngxshareddictlpop)\n* [rpop](#ngxshareddictrpop)\n* [llen](#ngxshareddictllen)\n* [ttl](#ngxshareddictttl)\n* [expire](#ngxshareddictexpire)\n* [flush_all](#ngxshareddictflush_all)\n* [flush_expired](#ngxshareddictflush_expired)\n* [get_keys](#ngxshareddictget_keys)\n* [capacity](#ngxshareddictcapacity)\n* [free_space](#ngxshareddictfree_space)\n\nAll these methods are *atomic* operations, that is, safe from concurrent accesses from multiple Nginx worker processes for the same `lua_shared_dict` zone.\n\nHere is an example:\n\n```nginx\n\n http {\n     lua_shared_dict dogs 10m;\n     server {\n         location /set {\n             content_by_lua_block {\n                 local dogs = ngx.shared.dogs\n                 dogs:set(\"Jim\", 8)\n                 ngx.say(\"STORED\")\n             }\n         }\n         location /get {\n             content_by_lua_block {\n                 local dogs = ngx.shared.dogs\n                 ngx.say(dogs:get(\"Jim\"))\n             }\n         }\n     }\n }\n```\n\nLet us test it:\n\n```bash\n\n $ curl localhost/set\n STORED\n\n $ curl localhost/get\n 8\n\n $ curl localhost/get\n 8\n```\n\nThe number `8` will be consistently output when accessing `/get` regardless of how many Nginx workers there are because the `dogs` dictionary resides in the shared memory and visible to *all* of the worker processes.\n\nThe shared dictionary will retain its contents through a server config reload (either by sending the `HUP` signal to the Nginx process or by using the `-s reload` command-line option).\n\nThe contents in the dictionary storage will be lost, however, when the Nginx server quits.\n\nThis feature was first introduced in the `v0.3.1rc22` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.get\n-------------------\n\n**syntax:** *value, flags = ngx.shared.DICT:get(key)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nRetrieving 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.\n\nIn case of errors, `nil` and a string describing the error will be returned.\n\nThe value returned will have the original data type when they were inserted into the dictionary, for example, Lua booleans, numbers, or strings.\n\nThe first argument to this method must be the dictionary object itself, for example,\n\n```lua\n\n local cats = ngx.shared.cats\n local value, flags = cats.get(cats, \"Marry\")\n```\n\nor use Lua's syntactic sugar for method calls:\n\n```lua\n\n local cats = ngx.shared.cats\n local value, flags = cats:get(\"Marry\")\n```\n\nThese two forms are fundamentally equivalent.\n\nIf the user flags is `0` (the default), then no flags value will be returned.\n\nThis feature was first introduced in the `v0.3.1rc22` release.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.get_stale\n-------------------------\n\n**syntax:** *value, flags, stale = ngx.shared.DICT:get_stale(key)*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSimilar to the [get](#ngxshareddictget) method but returns the value even if the key has already expired.\n\nReturns a 3rd value, `stale`, indicating whether the key has expired or not.\n\nNote that the value of an expired key is not guaranteed to be available so one should never rely on the availability of expired items.\n\nThis method was first introduced in the `0.8.6` release.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.set\n-------------------\n\n**syntax:** *success, err, forcible = ngx.shared.DICT:set(key, value, exptime?, flags?)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nUnconditionally sets a key-value pair into the shm-based dictionary [ngx.shared.DICT](#ngxshareddict). Returns three values:\n\n* `success`: boolean value to indicate whether the key-value pair is stored or not.\n* `err`: textual error message, can be `\"no memory\"`.\n* `forcible`: a boolean value to indicate whether other valid items have been removed forcibly when out of storage in the shared memory zone.\n\nThe `value` argument inserted can be Lua booleans, numbers, strings, or `nil`. Their value type will also be stored into the dictionary and the same data type can be retrieved later via the [get](#ngxshareddictget) method.\n\nThe optional `exptime` argument specifies expiration time (in seconds) for the inserted key-value pair. The time resolution is `0.001` seconds. If the `exptime` takes the value `0` (which is the default), then the item will never expire.\n\nThe optional `flags` argument specifies a user flags value associated with the entry to be stored. It can also be retrieved later with the value. The user flags is stored as an unsigned 32-bit integer internally. Defaults to `0`. The user flags argument was first introduced in the `v0.5.0rc2` release.\n\nWhen it fails to allocate memory for the current key-value item, then `set` will try removing existing items in the storage according to the Least-Recently Used (LRU) algorithm. Note that, LRU takes priority over expiration time here. If up to tens of existing items have been removed and the storage left is still insufficient (either due to the total capacity limit specified by [lua_shared_dict](#lua_shared_dict) or memory segmentation), then the `err` return value will be `no memory` and `success` will be `false`.\n\nIf the sizes of items in the dictionary are not multiples or even powers of a certain value (like 2), it is easier to encounter `no memory` error because of memory fragmentation. It is recommended to use different dictionaries for different sizes of items.\n\nWhen you encounter `no memory` error, you can also evict more least-recently-used items by retrying this method call more times to to make room for the current item.\n\nIf this method succeeds in storing the current item by forcibly removing other not-yet-expired items in the dictionary via LRU, the `forcible` return value will be `true`. If it stores the item without forcibly removing other valid items, then the return value `forcible` will be `false`.\n\nThe first argument to this method must be the dictionary object itself, for example,\n\n```lua\n\n local cats = ngx.shared.cats\n local succ, err, forcible = cats.set(cats, \"Marry\", \"it is a nice cat!\")\n```\n\nor use Lua's syntactic sugar for method calls:\n\n```lua\n\n local cats = ngx.shared.cats\n local succ, err, forcible = cats:set(\"Marry\", \"it is a nice cat!\")\n```\n\nThese two forms are fundamentally equivalent.\n\nThis feature was first introduced in the `v0.3.1rc22` release.\n\nPlease note that while internally the key-value pair is set atomically, the atomicity does not go across the method call boundary.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.safe_set\n------------------------\n\n**syntax:** *ok, err = ngx.shared.DICT:safe_set(key, value, exptime?, flags?)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSimilar 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\".\n\nThis feature was first introduced in the `v0.7.18` release.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.add\n-------------------\n\n**syntax:** *success, err, forcible = ngx.shared.DICT:add(key, value, exptime?, flags?)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nJust 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.\n\nIf the `key` argument already exists in the dictionary (and not expired for sure), the `success` return value will be `false` and the `err` return value will be `\"exists\"`.\n\nThis feature was first introduced in the `v0.3.1rc22` release.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.safe_add\n------------------------\n\n**syntax:** *ok, err = ngx.shared.DICT:safe_add(key, value, exptime?, flags?)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSimilar 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\".\n\nThis feature was first introduced in the `v0.7.18` release.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.replace\n-----------------------\n\n**syntax:** *success, err, forcible = ngx.shared.DICT:replace(key, value, exptime?, flags?)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nJust like the [set](#ngxshareddictset) method, but only stores the key-value pair into the dictionary [ngx.shared.DICT](#ngxshareddict) if the key *does* exist.\n\nIf the `key` argument does *not* exist in the dictionary (or expired already), the `success` return value will be `false` and the `err` return value will be `\"not found\"`.\n\nThis feature was first introduced in the `v0.3.1rc22` release.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.delete\n----------------------\n\n**syntax:** *ngx.shared.DICT:delete(key)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nUnconditionally removes the key-value pair from the shm-based dictionary [ngx.shared.DICT](#ngxshareddict).\n\nIt is equivalent to `ngx.shared.DICT:set(key, nil)`.\n\nThis feature was first introduced in the `v0.3.1rc22` release.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.incr\n--------------------\n\n**syntax:** *newval, err, forcible? = ngx.shared.DICT:incr(key, value, init?, init_ttl?)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\n**optional requirement:** `resty.core.shdict` or `resty.core`\n\nIncrements the (numerical) value for `key` in the shm-based dictionary [ngx.shared.DICT](#ngxshareddict) by the step value `value`. Returns the new resulting number if the operation is successfully completed or `nil` and an error message otherwise.\n\nWhen the key does not exist or has already expired in the shared dictionary,\n\n1. if the `init` argument is not specified or takes the value `nil`, this method will return `nil` and the error string `\"not found\"`, or\n1. if the `init` argument takes a number value, this method will create a new `key` with the value `init + value`.\n\nLike the [add](#ngxshareddictadd) method, it also overrides the (least recently used) unexpired items in the store when running out of storage in the shared memory zone.\n\nThe optional `init_ttl` argument specifies expiration time (in seconds) of the value when it is initialized via the `init` argument. The time resolution is `0.001` seconds. If `init_ttl` takes the value `0` (which is the default), then the item will never expire. This argument cannot be provided without providing the `init` argument as well, and has no effect if the value already exists (e.g., if it was previously inserted via [set](#ngxshareddictset) or the likes).\n\n**Note:** Usage of the `init_ttl` argument requires the `resty.core.shdict` or `resty.core` modules from the [lua-resty-core](https://github.com/openresty/lua-resty-core) library. Example:\n\n```lua\n\n require \"resty.core\"\n\n local cats = ngx.shared.cats\n local newval, err = cats:incr(\"black_cats\", 1, 0, 0.1)\n\n print(newval) -- 1\n\n ngx.sleep(0.2)\n\n local val, err = cats:get(\"black_cats\")\n print(val) -- nil\n```\n\nThe `forcible` return value will always be `nil` when the `init` argument is not specified.\n\nIf this method succeeds in storing the current item by forcibly removing other not-yet-expired items in the dictionary via LRU, the `forcible` return value will be `true`. If it stores the item without forcibly removing other valid items, then the return value `forcible` will be `false`.\n\nIf the original value is not a valid Lua number in the dictionary, it will return `nil` and `\"not a number\"`.\n\nThe `value` argument and `init` argument can be any valid Lua numbers, like negative numbers or floating-point numbers.\n\nThis method was first introduced in the `v0.3.1rc22` release.\n\nThe optional `init` parameter was first added in the `v0.10.6` release.\n\nThe optional `init_ttl` parameter was introduced in the `v0.10.12rc2` release.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.lpush\n---------------------\n\n**syntax:** *length, err = ngx.shared.DICT:lpush(key, value)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nInserts 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.\n\nIf `key` does not exist, it is created as an empty list before performing the push operation. When the `key` already takes a value that is not a list, it will return `nil` and `\"value not a list\"`.\n\nIt 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\".\n\nThis feature was first introduced in the `v0.10.6` release.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.rpush\n---------------------\n\n**syntax:** *length, err = ngx.shared.DICT:rpush(key, value)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSimilar to the [lpush](#ngxshareddictlpush) method, but inserts the specified (numerical or string) `value` at the tail of the list named `key`.\n\nThis feature was first introduced in the `v0.10.6` release.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.lpop\n--------------------\n\n**syntax:** *val, err = ngx.shared.DICT:lpop(key)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nRemoves and returns the first element of the list named `key` in the shm-based dictionary [ngx.shared.DICT](#ngxshareddict).\n\nIf `key` does not exist, it will return `nil`. When the `key` already takes a value that is not a list, it will return `nil` and `\"value not a list\"`.\n\nThis feature was first introduced in the `v0.10.6` release.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.rpop\n--------------------\n\n**syntax:** *val, err = ngx.shared.DICT:rpop(key)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nRemoves and returns the last element of the list named `key` in the shm-based dictionary [ngx.shared.DICT](#ngxshareddict).\n\nIf `key` does not exist, it will return `nil`. When the `key` already takes a value that is not a list, it will return `nil` and `\"value not a list\"`.\n\nThis feature was first introduced in the `v0.10.6` release.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.llen\n--------------------\n\n**syntax:** *len, err = ngx.shared.DICT:llen(key)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns the number of elements in the list named `key` in the shm-based dictionary [ngx.shared.DICT](#ngxshareddict).\n\nIf key does not exist, it is interpreted as an empty list and 0 is returned. When the `key` already takes a value that is not a list, it will return `nil` and `\"value not a list\"`.\n\nThis feature was first introduced in the `v0.10.6` release.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.ttl\n-------------------\n\n**syntax:** *ttl, err = ngx.shared.DICT:ttl(key)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\n**requires:** `resty.core.shdict` or `resty.core`\n\nRetrieves the remaining TTL (time-to-live in seconds) of a key-value pair in the shm-based dictionary [ngx.shared.DICT](#ngxshareddict). Returns the TTL as a number if the operation is successfully completed or `nil` and an error message otherwise.\n\nIf the key does not exist (or has already expired), this method will return `nil` and the error string `\"not found\"`.\n\nThe TTL is originally determined by the `exptime` argument of the [set](#ngxshareddictset), [add](#ngxshareddictadd), [replace](#ngxshareddictreplace) (and the likes) methods. It has a time resolution of `0.001` seconds. A value of `0` means that the item will never expire.\n\nExample:\n\n```lua\n\n require \"resty.core\"\n\n local cats = ngx.shared.cats\n local succ, err = cats:set(\"Marry\", \"a nice cat\", 0.5)\n\n ngx.sleep(0.2)\n\n local ttl, err = cats:ttl(\"Marry\")\n ngx.say(ttl) -- 0.3\n```\n\nThis feature was first introduced in the `v0.10.11` release.\n\n**Note:** This method requires the `resty.core.shdict` or `resty.core` modules from the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.expire\n----------------------\n\n**syntax:** *success, err = ngx.shared.DICT:expire(key, exptime)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\n**requires:** `resty.core.shdict` or `resty.core`\n\nUpdates the `exptime` (in second) of a key-value pair in the shm-based dictionary [ngx.shared.DICT](#ngxshareddict). Returns a boolean indicating success if the operation completes or `nil` and an error message otherwise.\n\nIf the key does not exist, this method will return `nil` and the error string `\"not found\"`.\n\nThe `exptime` argument has a resolution of `0.001` seconds. If `exptime` is `0`, then the item will never expire.\n\nExample:\n\n```lua\n\n require \"resty.core\"\n\n local cats = ngx.shared.cats\n local succ, err = cats:set(\"Marry\", \"a nice cat\", 0.1)\n\n succ, err = cats:expire(\"Marry\", 0.5)\n\n ngx.sleep(0.2)\n\n local val, err = cats:get(\"Marry\")\n ngx.say(val) -- \"a nice cat\"\n```\n\nThis feature was first introduced in the `v0.10.11` release.\n\n**Note:** This method requires the `resty.core.shdict` or `resty.core` modules from the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.flush_all\n-------------------------\n\n**syntax:** *ngx.shared.DICT:flush_all()*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nFlushes 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.\n\nThis feature was first introduced in the `v0.5.0rc17` release.\n\nSee also [ngx.shared.DICT.flush_expired](#ngxshareddictflush_expired) and [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.flush_expired\n-----------------------------\n\n**syntax:** *flushed = ngx.shared.DICT:flush_expired(max_count?)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nFlushes 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.\n\nUnlike the [flush_all](#ngxshareddictflush_all) method, this method actually frees up the memory used by the expired items.\n\nThis feature was first introduced in the `v0.6.3` release.\n\nSee also [ngx.shared.DICT.flush_all](#ngxshareddictflush_all) and [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.get_keys\n------------------------\n\n**syntax:** *keys = ngx.shared.DICT:get_keys(max_count?)*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nFetch a list of the keys from the dictionary, up to `<max_count>`.\n\nBy default, only the first 1024 keys (if any) are returned. When the `<max_count>` argument is given the value `0`, then all the keys will be returned even there is more than 1024 keys in the dictionary.\n\n**CAUTION** Avoid calling this method on dictionaries with a very large number of keys as it may lock the dictionary for significant amount of time and block Nginx worker processes trying to access the dictionary.\n\nThis feature was first introduced in the `v0.7.3` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.capacity\n------------------------\n\n**syntax:** *capacity_bytes = ngx.shared.DICT:capacity()*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\n**requires:** `resty.core.shdict` or `resty.core`\n\nRetrieves the capacity in bytes for the shm-based dictionary [ngx.shared.DICT](#ngxshareddict) declared with\nthe [lua_shared_dict](#lua_shared_dict) directive.\n\nExample:\n\n```lua\n\n require \"resty.core.shdict\"\n\n local cats = ngx.shared.cats\n local capacity_bytes = cats:capacity()\n```\n\nThis feature was first introduced in the `v0.10.11` release.\n\n**Note:** This method requires the `resty.core.shdict` or `resty.core` modules from the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.\n\nThis feature requires at least Nginx core version `0.7.3`.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.shared.DICT.free_space\n--------------------------\n\n**syntax:** *free_page_bytes = ngx.shared.DICT:free_space()*\n\n**context:** *init_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\n**requires:** `resty.core.shdict` or `resty.core`\n\nRetrieves the free page size in bytes for the shm-based dictionary [ngx.shared.DICT](#ngxshareddict).\n\n**Note:** The memory for ngx.shared.DICT is allocated via the Nginx slab allocator which has each slot for\ndata size ranges like \\~8, 9\\~16, 17\\~32, ..., 1025\\~2048, 2048\\~ bytes. And pages are assigned to a slot if there\nis no room in already assigned pages for the slot.\n\nSo even if the return value of the `free_space` method is zero, there may be room in already assigned pages, so\nyou may successfully set a new key value pair to the shared dict without getting `true` for `forcible` or\nnon nil `err` from the `ngx.shared.DICT.set`.\n\nOn the other hand, if already assigned pages for a slot are full and a new key value pair is added to the\nslot and there is no free page, you may get `true` for `forcible` or non nil `err` from the\n`ngx.shared.DICT.set` method.\n\nExample:\n\n```lua\n\n require \"resty.core.shdict\"\n\n local cats = ngx.shared.cats\n local free_page_bytes = cats:free_space()\n```\n\nThis feature was first introduced in the `v0.10.11` release.\n\n**Note:** This method requires the `resty.core.shdict` or `resty.core` modules from the [lua-resty-core](https://github.com/openresty/lua-resty-core) library.\n\nThis feature requires at least Nginx core version `1.11.7`.\n\nSee also [ngx.shared.DICT](#ngxshareddict).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.socket.udp\n--------------\n\n**syntax:** *udpsock = ngx.socket.udp()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nCreates 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:\n\n* [bind](#udpsockbind)\n* [setpeername](#udpsocksetpeername)\n* [send](#udpsocksend)\n* [receive](#udpsockreceive)\n* [close](#udpsockclose)\n* [settimeout](#udpsocksettimeout)\n\nIt is intended to be compatible with the UDP API of the [LuaSocket](http://w3.impa.br/~diego/software/luasocket/udp.html) library but is 100% nonblocking out of the box.\n\nThis feature was first introduced in the `v0.5.7` release.\n\nSee also [ngx.socket.tcp](#ngxsockettcp).\n\n[Back to TOC](#nginx-api-for-lua)\n\nudpsock:bind\n------------\n**syntax:** *ok, err = udpsock:bind(address)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;,ssl_session_fetch_by_lua&#42;,ssl_client_hello_by_lua&#42;*\n\nJust 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.\n\nOnly IP addresses can be specified as the `address` argument.\n\nHere is an example for connecting to a TCP server from the specified local IP address:\n\n```nginx\n\n location /test {\n     content_by_lua_block {\n         local sock = ngx.socket.udp()\n         -- assume \"192.168.1.10\" is the local ip address\n         local ok, err = sock:bind(\"192.168.1.10\")\n         if not ok then\n             ngx.say(\"failed to bind: \", err)\n             return\n         end\n         sock:close()\n     }\n }\n```\n\n[Back to TOC](#nginx-api-for-lua)\n\nudpsock:setpeername\n-------------------\n\n**syntax:** *ok, err = udpsock:setpeername(host, port)*\n\n**syntax:** *ok, err = udpsock:setpeername(\"unix:/path/to/unix-domain.socket\")*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nAttempts 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.\n\nBoth IP addresses and domain names can be specified as the `host` argument. In case of domain names, this method will use Nginx core's dynamic resolver to parse the domain name without blocking and it is required to configure the [resolver](http://nginx.org/en/docs/http/ngx_http_core_module.html#resolver) directive in the `nginx.conf` file like this:\n\n```nginx\n\n resolver 8.8.8.8;  # use Google's public DNS nameserver\n```\n\nIf the nameserver returns multiple IP addresses for the host name, this method will pick up one randomly.\n\nIn case of error, the method returns `nil` followed by a string describing the error. In case of success, the method returns `1`.\n\nHere is an example for connecting to a UDP (memcached) server:\n\n```nginx\n\n location /test {\n     resolver 8.8.8.8;\n\n     content_by_lua_block {\n         local sock = ngx.socket.udp()\n         local ok, err = sock:setpeername(\"my.memcached.server.domain\", 11211)\n         if not ok then\n             ngx.say(\"failed to connect to memcached: \", err)\n             return\n         end\n         ngx.say(\"successfully connected to memcached!\")\n         sock:close()\n     }\n }\n```\n\nSince the `v0.7.18` release, connecting to a datagram unix domain socket file is also possible on Linux:\n\n```lua\n\n local sock = ngx.socket.udp()\n local ok, err = sock:setpeername(\"unix:/tmp/some-datagram-service.sock\")\n if not ok then\n     ngx.say(\"failed to connect to the datagram unix domain socket: \", err)\n     return\n end\n\n -- do something after connect\n -- such as sock:send or sock:receive\n```\n\nassuming the datagram service is listening on the unix domain socket file `/tmp/some-datagram-service.sock` and the client socket will use the \"autobind\" feature on Linux.\n\nCalling this method on an already connected socket object will cause the original connection to be closed first.\n\nThis method was first introduced in the `v0.5.7` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nudpsock:send\n------------\n\n**syntax:** *ok, err = udpsock:send(data)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSends data on the current UDP or datagram unix domain socket object.\n\nIn case of success, it returns `1`. Otherwise, it returns `nil` and a string describing the error.\n\nThe input argument `data` can either be a Lua string or a (nested) Lua table holding string fragments. In case of table arguments, this method will copy all the string elements piece by piece to the underlying Nginx socket send buffers, which is usually optimal than doing string concatenation operations on the Lua land.\n\nThis feature was first introduced in the `v0.5.7` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nudpsock:receive\n---------------\n\n**syntax:** *data, err = udpsock:receive(size?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReceives data from the UDP or datagram unix domain socket object with an optional receive buffer size argument, `size`.\n\nThis method is a synchronous operation and is 100% nonblocking.\n\nIn case of success, it returns the data received; in case of error, it returns `nil` with a string describing the error.\n\nIf the `size` argument is specified, then this method will use this size as the receive buffer size. But when this size is greater than `8192`, then `8192` will be used instead.\n\nIf no argument is specified, then the maximal buffer size, `8192` is assumed.\n\nTimeout for the reading operation is controlled by the [lua_socket_read_timeout](#lua_socket_read_timeout) config directive and the [settimeout](#udpsocksettimeout) method. And the latter takes priority. For example:\n\n```lua\n\n sock:settimeout(1000)  -- one second timeout\n local data, err = sock:receive()\n if not data then\n     ngx.say(\"failed to read a packet: \", err)\n     return\n end\n ngx.say(\"successfully read a packet: \", data)\n```\n\nIt is important here to call the [settimeout](#udpsocksettimeout) method *before* calling this method.\n\nThis feature was first introduced in the `v0.5.7` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nudpsock:close\n-------------\n\n**syntax:** *ok, err = udpsock:close()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nCloses 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.\n\nSocket objects that have not invoked this method (and associated connections) will be closed when the socket object is released by the Lua GC (Garbage Collector) or the current client HTTP request finishes processing.\n\nThis feature was first introduced in the `v0.5.7` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nudpsock:settimeout\n------------------\n\n**syntax:** *udpsock:settimeout(time)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSet the timeout value in milliseconds for subsequent socket operations (like [receive](#udpsockreceive)).\n\nSettings done by this method takes priority over those config directives, like [lua_socket_read_timeout](#lua_socket_read_timeout).\n\nThis feature was first introduced in the `v0.5.7` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.socket.stream\n-----------------\n\nJust an alias to [ngx.socket.tcp](#ngxsockettcp). If the stream-typed cosocket may also connect to a unix domain\nsocket, then this API name is preferred.\n\nThis API function was first added to the `v0.10.1` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.socket.tcp\n--------------\n\n**syntax:** *tcpsock = ngx.socket.tcp()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nCreates 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:\n\n* [bind](#tcpsockbind)\n* [connect](#tcpsockconnect)\n* [setclientcert](#tcpsocksetclientcert)\n* [sslhandshake](#tcpsocksslhandshake)\n* [send](#tcpsocksend)\n* [receive](#tcpsockreceive)\n* [close](#tcpsockclose)\n* [settimeout](#tcpsocksettimeout)\n* [settimeouts](#tcpsocksettimeouts)\n* [setoption](#tcpsocksetoption)\n* [receiveany](#tcpsockreceiveany)\n* [receiveuntil](#tcpsockreceiveuntil)\n* [setkeepalive](#tcpsocksetkeepalive)\n* [getreusedtimes](#tcpsockgetreusedtimes)\n* [tcpsock:getsslpointer](#tcpsockgetsslpointer)\n* [tcpsock:getsslctx](#tcpsockgetsslctx)\n* [tcpsock:getsslsession](#tcpsockgetsslsession)\n\nIt is intended to be compatible with the TCP API of the [LuaSocket](http://w3.impa.br/~diego/software/luasocket/tcp.html) library but is 100% nonblocking out of the box. Also, we introduce some new APIs to provide more functionalities.\n\nThe cosocket object created by this API function has exactly the same lifetime as the Lua handler creating it. So never pass the cosocket object to any other Lua handler (including ngx.timer callback functions) and never share the cosocket object between different Nginx requests.\n\nFor every cosocket object's underlying connection, if you do not\nexplicitly close it (via [close](#tcpsockclose)) or put it back to the connection\npool (via [setkeepalive](#tcpsocksetkeepalive)), then it is automatically closed when one of\nthe following two events happens:\n\n* the current request handler completes, or\n* the Lua cosocket object value gets collected by the Lua GC.\n\nFatal errors in cosocket operations always automatically close the current\nconnection (note that, read timeout error is the only error that is\nnot fatal), and if you call [close](#tcpsockclose) on a closed connection, you will get\nthe \"closed\" error.\n\nStarting from the `0.9.9` release, the cosocket object here is full-duplex, that is, a reader \"light thread\" and a writer \"light thread\" can operate on a single cosocket object simultaneously (both \"light threads\" must belong to the same Lua handler though, see reasons above). But you cannot have two \"light threads\" both reading (or writing or connecting) the same cosocket, otherwise you might get an error like \"socket busy reading\" when calling the methods of the cosocket object.\n\nThis feature was first introduced in the `v0.5.0rc1` release.\n\nSee also [ngx.socket.udp](#ngxsocketudp).\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:bind\n------------\n**syntax:** *ok, err = tcpsock:bind(address, port?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;,ssl_session_fetch_by_lua&#42;,ssl_client_hello_by_lua&#42;*\n\nJust 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.\n\nIP addresses can be specified as the `address` argument.\nThe optional `port` argument is usually used in the transparent proxy.\n\nHere is an example for connecting to a TCP server from the specified local IP address:\n\n```nginx\n\n location /test {\n     content_by_lua_block {\n         local sock = ngx.socket.tcp()\n         -- assume \"192.168.1.10\" is the local ip address\n         local ok, err = sock:bind(\"192.168.1.10\")\n         if not ok then\n             ngx.say(\"failed to bind\")\n             return\n         end\n         local ok, err = sock:connect(\"192.168.1.67\", 80)\n         if not ok then\n             ngx.say(\"failed to connect server: \", err)\n             return\n         end\n         ngx.say(\"successfully connected!\")\n         sock:close()\n     }\n }\n```\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:connect\n---------------\n\n**syntax:** *ok, err = tcpsock:connect(host, port, options_table?)*\n\n**syntax:** *ok, err = tcpsock:connect(\"unix:/path/to/unix-domain.socket\", options_table?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nAttempts to connect a TCP socket object to a remote server or to a stream unix domain socket file without blocking.\n\nBefore actually resolving the host name and connecting to the remote backend, this method will always look up the connection pool for matched idle connections created by previous calls of this method (or the [ngx.socket.connect](#ngxsocketconnect) function).\n\nBoth IP addresses and domain names can be specified as the `host` argument. In case of domain names, this method will use Nginx core's dynamic resolver to parse the domain name without blocking and it is required to configure the [resolver](http://nginx.org/en/docs/http/ngx_http_core_module.html#resolver) directive in the `nginx.conf` file like this:\n\n```nginx\n\n resolver 8.8.8.8;  # use Google's public DNS nameserver\n```\n\nIf the nameserver returns multiple IP addresses for the host name, this method will pick up one randomly.\n\nIn case of error, the method returns `nil` followed by a string describing the error. In case of success, the method returns `1`.\n\nHere is an example for connecting to a TCP server:\n\n```nginx\n\n location /test {\n     resolver 8.8.8.8;\n\n     content_by_lua_block {\n         local sock = ngx.socket.tcp()\n         local ok, err = sock:connect(\"www.google.com\", 80)\n         if not ok then\n             ngx.say(\"failed to connect to google: \", err)\n             return\n         end\n         ngx.say(\"successfully connected to google!\")\n         sock:close()\n     }\n }\n```\n\nConnecting to a Unix Domain Socket file is also possible:\n\n```lua\n\n local sock = ngx.socket.tcp()\n local ok, err = sock:connect(\"unix:/tmp/memcached.sock\")\n if not ok then\n     ngx.say(\"failed to connect to the memcached unix domain socket: \", err)\n     return\n end\n\n -- do something after connect\n -- such as sock:send or sock:receive\n```\n\nassuming memcached (or something else) is listening on the unix domain socket file `/tmp/memcached.sock`.\n\nTimeout for the connecting operation is controlled by the [lua_socket_connect_timeout](#lua_socket_connect_timeout) config directive and the [settimeout](#tcpsocksettimeout) method. And the latter takes priority. For example:\n\n```lua\n\n local sock = ngx.socket.tcp()\n sock:settimeout(1000)  -- one second timeout\n local ok, err = sock:connect(host, port)\n```\n\nIt is important here to call the [settimeout](#tcpsocksettimeout) method *before* calling this method.\n\nCalling this method on an already connected socket object will cause the original connection to be closed first.\n\nAn optional Lua table can be specified as the last argument to this method to specify various connect options:\n\n* `pool`\n\tspecify a custom name for the connection pool being used. If omitted, then the connection pool name will be generated from the string template `\"<host>:<port>\"` or `\"<unix-socket-path>\"`.\n\n* `pool_size`\n\tspecify the size of the connection pool. If omitted and no\n\t`backlog` option was provided, no pool will be created. If omitted\n\tbut `backlog` was provided, the pool will be created with a default\n\tsize equal to the value of the [lua_socket_pool_size](#lua_socket_pool_size)\n\tdirective.\n\tThe connection pool holds up to `pool_size` alive connections\n\tready to be reused by subsequent calls to [connect](#tcpsockconnect), but\n\tnote that there is no upper limit to the total number of opened connections\n\toutside of the pool. If you need to restrict the total number of opened\n\tconnections, specify the `backlog` option.\n\tWhen the connection pool would exceed its size limit, the least recently used\n\t(kept-alive) connection already in the pool will be closed to make room for\n\tthe current connection.\n\tNote that the cosocket connection pool is per Nginx worker process rather\n\tthan per Nginx server instance, so the size limit specified here also applies\n\tto every single Nginx worker process. Also note that the size of the connection\n\tpool cannot be changed once it has been created.\n\tThis option was first introduced in the `v0.10.14` release.\n\n* `backlog`\n\tif specified, this module will limit the total number of opened connections\n\tfor this pool. No more connections than `pool_size` can be opened\n\tfor this pool at any time. If `pool_size` number of connections are in use,\n\tsubsequent connect operations will be queued into a queue equal to this\n\toption's value (the \"backlog\" queue).\n\tIf the number of queued connect operations is equal to `backlog`,\n\tsubsequent connect operations will fail and return `nil` plus the\n\terror string `\"too many waiting connect operations\"`.\n\tThe queued connect operations will be resumed once the number of active\n\tconnections becomes less than `pool_size`.\n\tThe queued connect operation will abort once they have been queued for more\n\tthan `connect_timeout`, controlled by\n\t[settimeouts](#tcpsocksettimeouts), and will return `nil` plus\n\tthe error string `\"timeout\"`.\n\tThis option was first introduced in the `v0.10.14` release.\n\nThe support for the options table argument was first introduced in the `v0.5.7` release.\n\nThis method was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\n\ntcpsock:getfd\n--------------------\n\n**syntax:** *fd, err = tcpsock:getfd()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nGet the file descriptor of the current tcp socket.\n\nThis method was first introduced in the `v0.10.29` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\n\ntcpsock:setclientcert\n---------------------\n\n**syntax:** *ok, err = tcpsock:setclientcert(cert, pkey)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSet client certificate chain and corresponding private key to the TCP socket object.\nThe certificate chain and private key provided will be used later by the [tcpsock:sslhandshake](#tcpsocksslhandshake) method.\n\n* `cert` specify a client certificate chain cdata object that will be used while handshaking with\nremote 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)\nfunction provided by lua-resty-core. Note that specifying the `cert` option requires\ncorresponding `pkey` be provided too. See below.\n* `pkey` specify a private key corresponds to the `cert` option above.\nThese 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)\nfunction provided by lua-resty-core.\n\nIf both of `cert` and `pkey` are `nil`, this method will clear any existing client certificate and private key\nthat was previously set on the cosocket object.\n\nThis method was first introduced in the `v0.10.22` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:sslhandshake\n--------------------\n\n**syntax:** *session, err = tcpsock:sslhandshake(reused_session?, server_name?, ssl_verify?, send_status_req?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nDoes SSL/TLS handshake on the currently established connection.\n\nThe optional `reused_session` argument can take a former SSL\nsession userdata returned by a previous `sslhandshake`\ncall for exactly the same target. For short-lived connections, reusing SSL\nsessions can usually speed up the handshake by one order by magnitude but it\nis not so useful if the connection pool is enabled. This argument defaults to\n`nil`. If this argument takes the boolean `false` value, no SSL session\nuserdata would return by this call and only a Lua boolean will be returned as\nthe first return value; otherwise the current SSL session will\nalways be returned as the first argument in case of successes.\n\nThe optional `server_name` argument is used to specify the server\nname for the new TLS extension Server Name Indication (SNI). Use of SNI can\nmake different servers share the same IP address on the server side. Also,\nwhen SSL verification is enabled, this `server_name` argument is\nalso used to validate the server name specified in the server certificate sent from\nthe remote.\n\nThe optional `ssl_verify` argument takes a Lua boolean value to\ncontrol whether to perform SSL verification. When set to `true`, the server\ncertificate will be verified according to the CA certificates specified by\nthe [lua_ssl_trusted_certificate](#lua_ssl_trusted_certificate) directive.\nYou may also need to adjust the [lua_ssl_verify_depth](#lua_ssl_verify_depth)\ndirective to control how deep we should follow along the certificate chain.\nAlso, when the `ssl_verify` argument is true and the\n`server_name` argument is also specified, the latter will be used\nto validate the server name in the server certificate.\n\nThe optional `send_status_req` argument takes a boolean that controls whether to send\nthe OCSP status request in the SSL handshake request (which is for requesting OCSP stapling).\n\nFor connections that have already done SSL/TLS handshake, this method returns\nimmediately.\n\nThis method was first introduced in the `v0.9.11` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:getsslpointer\n--------------------\n\n**syntax:** *sslpointer, err = tcpsock:getsslpointer()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nRetrieves the underlying SSL pointer (SSL_CTX structure) of the cosocket connection.\n\nThis method provides access to the raw OpenSSL SSL pointer, which is useful when third-party modules or FFI code need to perform low-level SSL operations directly on the connection. This enables cross-module operations and advanced SSL manipulations that are not exposed through the standard cosocket API.\n\nOn success, returns the SSL pointer as a light userdata that can be passed to C functions via FFI. On failure, returns `nil` and a string describing the error.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:getsslctx\n--------------------\n\n**syntax:** *sslctx, err = tcpsock:getsslctx()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nRetrieves the underlying SSL pointer (SSL_CTX structure) of the cosocket connection.\n\nThis method provides access to the raw OpenSSL SSL pointer, which is useful when third-party modules or FFI code need to perform low-level SSL operations directly on the connection. This enables cross-module operations and advanced SSL manipulations that are not exposed through the standard cosocket API.\n\nOn success, returns the SSL pointer as a light userdata that can be passed to C functions via FFI. On failure, returns `nil` and a string describing the error.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:getsslsession\n-----------------------\n\n**syntax:** *session, err = tcpsock:getsslsession()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nRetrieves the SSL session object from the cosocket connection for session resumption purposes.\n\nWhile `tcpsock:sslhandshake()` also returns an SSL session, the server may not have sent the session resumption ticket to the client yet at that point, making the session non-reusable. By calling `getsslsession` after the request completes, you can obtain an SSL session that is more likely to be reusable for future connections. This session can then be passed to subsequent `sslhandshake()` calls to enable SSL session resumption, which reduces handshake overhead and improves connection performance.\n\nOn success, returns the SSL session as a light userdata. On failure, returns `nil` and a string describing the error.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:send\n------------\n\n**syntax:** *bytes, err = tcpsock:send(data)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSends data without blocking on the current TCP or Unix Domain Socket connection.\n\nThis method is a synchronous operation that will not return until *all* the data has been flushed into the system socket send buffer or an error occurs.\n\nIn case of success, it returns the total number of bytes that have been sent. Otherwise, it returns `nil` and a string describing the error.\n\nThe input argument `data` can either be a Lua string or a (nested) Lua table holding string fragments. In case of table arguments, this method will copy all the string elements piece by piece to the underlying Nginx socket send buffers, which is usually optimal than doing string concatenation operations on the Lua land.\n\nTimeout for the sending operation is controlled by the [lua_socket_send_timeout](#lua_socket_send_timeout) config directive and the [settimeout](#tcpsocksettimeout) method. And the latter takes priority. For example:\n\n```lua\n\n sock:settimeout(1000)  -- one second timeout\n local bytes, err = sock:send(request)\n```\n\nIt is important here to call the [settimeout](#tcpsocksettimeout) method *before* calling this method.\n\nIn case of any connection errors, this method always automatically closes the current connection.\n\nThis feature was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:receive\n---------------\n\n**syntax:** *data, err, partial = tcpsock:receive(size)*\n\n**syntax:** *data, err, partial = tcpsock:receive(pattern?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReceives data from the connected socket according to the reading pattern or size.\n\nThis method is a synchronous operation just like the [send](#tcpsocksend) method and is 100% nonblocking.\n\nIn case of success, it returns the data received; in case of error, it returns `nil` with a string describing the error and the partial data received so far.\n\nIf a number-like argument is specified (including strings that look like numbers), then it is interpreted as a size. This method will not return until it reads exactly this size of data or an error occurs.\n\nIf a non-number-like string argument is specified, then it is interpreted as a \"pattern\". The following patterns are supported:\n\n* `'*a'`: reads from the socket until the connection is closed. No end-of-line translation is performed;\n* `'*l'`: reads a line of text from the socket. The line is terminated by a `Line Feed` (LF) character (ASCII 10), optionally preceded by a `Carriage Return` (CR) character (ASCII 13). The CR and LF characters are not included in the returned line. In fact, all CR characters are ignored by the pattern.\n\nIf no argument is specified, then it is assumed to be the pattern `'*l'`, that is, the line reading pattern.\n\nTimeout for the reading operation is controlled by the [lua_socket_read_timeout](#lua_socket_read_timeout) config directive and the [settimeout](#tcpsocksettimeout) method. And the latter takes priority. For example:\n\n```lua\n\n sock:settimeout(1000)  -- one second timeout\n local line, err, partial = sock:receive()\n if not line then\n     ngx.say(\"failed to read a line: \", err)\n     return\n end\n ngx.say(\"successfully read a line: \", line)\n```\n\nIt is important here to call the [settimeout](#tcpsocksettimeout) method *before* calling this method.\n\nSince the `v0.8.8` release, this method no longer automatically closes the current connection when the read timeout error happens. For other connection errors, this method always automatically closes the connection.\n\nThis feature was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:receiveany\n------------------\n\n**syntax:** *data, err = tcpsock:receiveany(max)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns any data received by the connected socket, at most `max` bytes.\n\nThis method is a synchronous operation just like the [send](#tcpsocksend) method and is 100% nonblocking.\n\nIn case of success, it returns the data received; in case of error, it returns `nil` with a string describing the error.\n\nIf the received data is more than this size, this method will return with exactly this size of data.\nThe remaining data in the underlying receive buffer could be returned in the next reading operation.\n\nTimeout for the reading operation is controlled by the [lua_socket_read_timeout](#lua_socket_read_timeout) config directive and the [settimeouts](#tcpsocksettimeouts) method. And the latter takes priority. For example:\n\n```lua\n\n sock:settimeouts(1000, 1000, 1000)  -- one second timeout for connect/read/write\n local data, err = sock:receiveany(10 * 1024) -- read any data, at most 10K\n if not data then\n     ngx.say(\"failed to read any data: \", err)\n     return\n end\n ngx.say(\"successfully read: \", data)\n```\n\nThis method doesn't automatically close the current connection when the read timeout error occurs. For other connection errors, this method always automatically closes the connection.\n\nThis feature was first introduced in the `v0.10.14` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:receiveuntil\n--------------------\n\n**syntax:** *iterator = tcpsock:receiveuntil(pattern, options?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nThis 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.\n\nHere is an example for using this method to read a data stream with the boundary sequence `--abcedhb`:\n\n```lua\n\n local reader = sock:receiveuntil(\"\\r\\n--abcedhb\")\n local data, err, partial = reader()\n if not data then\n     ngx.say(\"failed to read the data stream: \", err)\n end\n ngx.say(\"read the data stream: \", data)\n```\n\nWhen called without any argument, the iterator function returns the received data right *before* the specified pattern string in the incoming data stream. So for the example above, if the incoming data stream is `'hello, world! -agentzh\\r\\n--abcedhb blah blah'`, then the string `'hello, world! -agentzh'` will be returned.\n\nIn case of error, the iterator function will return `nil` along with a string describing the error and the partial data bytes that have been read so far.\n\nThe iterator function can be called multiple times and can be mixed safely with other cosocket method calls or other iterator function calls.\n\nThe iterator function behaves differently (i.e., like a real iterator) when it is called with a `size` argument. That is, it will read that `size` of data on each invocation and will return `nil` at the last invocation (either sees the boundary pattern or meets an error). For the last successful invocation of the iterator function, the `err` return value will be `nil` too. The iterator function will be reset after the last successful invocation that returns `nil` data and `nil` error. Consider the following example:\n\n```lua\n\n local reader = sock:receiveuntil(\"\\r\\n--abcedhb\")\n\n while true do\n     local data, err, partial = reader(4)\n     if not data then\n         if err then\n             ngx.say(\"failed to read the data stream: \", err)\n             break\n         end\n\n         ngx.say(\"read done\")\n         break\n     end\n     ngx.say(\"read chunk: [\", data, \"]\")\n end\n```\n\nThen for the incoming data stream `'hello, world! -agentzh\\r\\n--abcedhb blah blah'`, we shall get the following output from the sample code above:\n\n\n    read chunk: [hell]\n    read chunk: [o, w]\n    read chunk: [orld]\n    read chunk: [! -a]\n    read chunk: [gent]\n    read chunk: [zh]\n    read done\n\n\nNote that, the actual data returned *might* be a little longer than the size limit specified by the `size` argument when the boundary pattern has ambiguity for streaming parsing. Near the boundary of the data stream, the data string actually returned could also be shorter than the size limit.\n\nTimeout for the iterator function's reading operation is controlled by the [lua_socket_read_timeout](#lua_socket_read_timeout) config directive and the [settimeout](#tcpsocksettimeout) method. And the latter takes priority. For example:\n\n```lua\n\n local readline = sock:receiveuntil(\"\\r\\n\")\n\n sock:settimeout(1000)  -- one second timeout\n line, err, partial = readline()\n if not line then\n     ngx.say(\"failed to read a line: \", err)\n     return\n end\n ngx.say(\"successfully read a line: \", line)\n```\n\nIt is important here to call the [settimeout](#tcpsocksettimeout) method *before* calling the iterator function (note that the `receiveuntil` call is irrelevant here).\n\nAs from the `v0.5.1` release, this method also takes an optional `options` table argument to control the behavior. The following options are supported:\n\n* `inclusive`\n\nThe `inclusive` takes a boolean value to control whether to include the pattern string in the returned data string. Default to `false`. For example,\n\n```lua\n\n local reader = tcpsock:receiveuntil(\"_END_\", { inclusive = true })\n local data = reader()\n ngx.say(data)\n```\n\nThen for the input data stream `\"hello world _END_ blah blah blah\"`, then the example above will output `hello world _END_`, including the pattern string `_END_` itself.\n\nSince the `v0.8.8` release, this method no longer automatically closes the current connection when the read timeout error happens. For other connection errors, this method always automatically closes the connection.\n\nThis method was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:close\n-------------\n\n**syntax:** *ok, err = tcpsock:close()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nCloses 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.\n\nNote that there is no need to call this method on socket objects that have invoked the [setkeepalive](#tcpsocksetkeepalive) method because the socket object is already closed (and the current connection is saved into the built-in connection pool).\n\nSocket objects that have not invoked this method (and associated connections) will be closed when the socket object is released by the Lua GC (Garbage Collector) or the current client HTTP request finishes processing.\n\nThis feature was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:settimeout\n------------------\n\n**syntax:** *tcpsock:settimeout(time)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSet the timeout value in milliseconds for subsequent socket operations ([connect](#tcpsockconnect), [receive](#tcpsockreceive), and iterators returned from [receiveuntil](#tcpsockreceiveuntil)).\n\nSettings done by this method take priority over those specified via config directives (i.e. [lua_socket_connect_timeout](#lua_socket_connect_timeout), [lua_socket_send_timeout](#lua_socket_send_timeout), and [lua_socket_read_timeout](#lua_socket_read_timeout)).\n\nNote that this method does *not* affect the [lua_socket_keepalive_timeout](#lua_socket_keepalive_timeout) setting; the `timeout` argument to the [setkeepalive](#tcpsocksetkeepalive) method should be used for this purpose instead.\n\nThis feature was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:settimeouts\n-------------------\n\n**syntax:** *tcpsock:settimeouts(connect_timeout, send_timeout, read_timeout)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nRespectively sets the connect, send, and read timeout thresholds (in milliseconds) for subsequent socket\noperations ([connect](#tcpsockconnect), [send](#tcpsocksend), [receive](#tcpsockreceive), and iterators returned from [receiveuntil](#tcpsockreceiveuntil)).\n\nSettings done by this method take priority over those specified via config directives (i.e. [lua_socket_connect_timeout](#lua_socket_connect_timeout), [lua_socket_send_timeout](#lua_socket_send_timeout), and [lua_socket_read_timeout](#lua_socket_read_timeout)).\n\nIt is recommended to use [settimeouts](#tcpsocksettimeouts) instead of [settimeout](#tcpsocksettimeout).\n\nNote that this method does *not* affect the [lua_socket_keepalive_timeout](#lua_socket_keepalive_timeout) setting; the `timeout` argument to the [setkeepalive](#tcpsocksetkeepalive) method should be used for this purpose instead.\n\nThis feature was first introduced in the `v0.10.7` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:setoption\n-----------------\n\n**syntax:** *ok, err = tcpsock:setoption(option, value?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nThis function is added for [LuaSocket](http://w3.impa.br/~diego/software/luasocket/tcp.html) API compatibility, its functionality is implemented `v0.10.18`.\n\nThis feature was first introduced in the `v0.5.0rc1` release.\n\nIn case of success, it returns `true`. Otherwise, it returns nil and a string describing the error.\n\nThe `option` is a string with the option name, and the value depends on the option being set:\n\n* `keepalive`\n\n\tSetting this option to true enables sending of keep-alive messages on\n\tconnection-oriented sockets. Make sure the `connect` function\n\thad been called before, for example,\n\n    ```lua\n\n    local ok, err = tcpsock:setoption(\"keepalive\", true)\n    if not ok then\n        ngx.say(\"setoption keepalive failed: \", err)\n    end\n    ```\n* `reuseaddr`\n\n\tEnabling this option indicates that the rules used in validating addresses\n\tsupplied in a call to bind should allow reuse of local addresses. Make sure\n\tthe `connect` function had been called before, for example,\n\n    ```lua\n\n    local ok, err = tcpsock:setoption(\"reuseaddr\", 0)\n    if not ok then\n        ngx.say(\"setoption reuseaddr failed: \", err)\n    end\n    ```\n* `tcp-nodelay`\n\n\tSetting this option to true disables the Nagle's algorithm for the connection.\n\tMake sure the `connect` function had been called before, for example,\n\n    ```lua\n\n    local ok, err = tcpsock:setoption(\"tcp-nodelay\", true)\n    if not ok then\n        ngx.say(\"setoption tcp-nodelay failed: \", err)\n    end\n    ```\n* `sndbuf`\n\n\tSets the maximum socket send buffer in bytes. The kernel doubles this value\n\t(to allow space for bookkeeping overhead) when it is set using setsockopt().\n\tMake sure the `connect` function had been called before, for example,\n\n    ```lua\n\n    local ok, err = tcpsock:setoption(\"sndbuf\", 1024 * 10)\n    if not ok then\n        ngx.say(\"setoption sndbuf failed: \", err)\n    end\n    ```\n* `rcvbuf`\n\n\tSets the maximum socket receive buffer in bytes. The kernel doubles this value\n\t(to allow space for bookkeeping overhead) when it is set using setsockopt. Make\n\tsure the `connect` function had been called before, for example,\n\n    ```lua\n\n    local ok, err = tcpsock:setoption(\"rcvbuf\", 1024 * 10)\n    if not ok then\n        ngx.say(\"setoption rcvbuf failed: \", err)\n    end\n    ```\n\nNOTE: Once the option is set, it will become effective until the connection is closed. If you know the connection is from the connection pool and all the in-pool connections already have called the setoption() method with the desired socket option state, then you can just skip calling setoption() again to avoid the overhead of repeated calls, for example,\n\n```lua\n\n local count, err = tcpsock:getreusedtimes()\n if not count then\n     ngx.say(\"getreusedtimes failed: \", err)\n     return\n end\n\n if count == 0 then\n     local ok, err = tcpsock:setoption(\"rcvbuf\", 1024 * 10)\n     if not ok then\n         ngx.say(\"setoption rcvbuf failed: \", err)\n         return\n     end\n end\n```\n\nThese options described above are supported in `v0.10.18`, and more options will be implemented in future.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:setkeepalive\n--------------------\n\n**syntax:** *ok, err = tcpsock:setkeepalive(timeout?, size?)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nPuts 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.\n\nThe first optional argument, `timeout`, can be used to specify the maximal idle timeout (in milliseconds) for the current connection. If omitted, the default setting in the [lua_socket_keepalive_timeout](#lua_socket_keepalive_timeout) config directive will be used. If the `0` value is given, then the timeout interval is unlimited.\n\nThe second optional argument `size` is considered deprecated since\nthe `v0.10.14` release of this module, in favor of the\n`pool_size` option of the [connect](#tcpsockconnect) method.\nSince the `v0.10.14` release, this option will only take effect if\nthe call to [connect](#tcpsockconnect) did not already create a connection\npool.\nWhen this option takes effect (no connection pool was previously created by\n[connect](#tcpsockconnect)), it will specify the size of the connection pool,\nand create it.\nIf omitted (and no pool was previously created), the default size is the value\nof the [lua_socket_pool_size](#lua_socket_pool_size) directive.\nThe connection pool holds up to `size` alive connections ready to be\nreused by subsequent calls to [connect](#tcpsockconnect), but note that there\nis no upper limit to the total number of opened connections outside of the\npool.\nWhen the connection pool would exceed its size limit, the least recently used\n(kept-alive) connection already in the pool will be closed to make room for\nthe current connection.\nNote that the cosocket connection pool is per Nginx worker process rather\nthan per Nginx server instance, so the size limit specified here also applies\nto every single Nginx worker process. Also note that the size of the connection\npool cannot be changed once it has been created.\nIf you need to restrict the total number of opened connections, specify both\nthe `pool_size` and `backlog` option in the call to\n[connect](#tcpsockconnect).\n\nIn case of success, this method returns `1`; otherwise, it returns `nil` and a string describing the error.\n\nWhen the system receive buffer for the current connection has unread data, then this method will return the \"connection in dubious state\" error message (as the second return value) because the previous session has unread data left behind for the next session and the connection is not safe to be reused.\n\nThis method also makes the current cosocket object enter the \"closed\" state, so there is no need to manually call the [close](#tcpsockclose) method on it afterwards.\n\nThis feature was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ntcpsock:getreusedtimes\n----------------------\n\n**syntax:** *count, err = tcpsock:getreusedtimes()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nThis method returns the (successfully) reused times for the current connection. In case of error, it returns `nil` and a string describing the error.\n\nIf the current connection does not come from the built-in connection pool, then this method always returns `0`, that is, the connection has never been reused (yet). If the connection comes from the connection pool, then the return value is always non-zero. So this method can also be used to determine if the current connection comes from the pool.\n\nThis feature was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.socket.connect\n------------------\n\n**syntax:** *tcpsock, err = ngx.socket.connect(host, port)*\n\n**syntax:** *tcpsock, err = ngx.socket.connect(\"unix:/path/to/unix-domain.socket\")*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;*\n\nThis function is a shortcut for combining [ngx.socket.tcp()](#ngxsockettcp) and the [connect()](#tcpsockconnect) method call in a single operation. It is actually implemented like this:\n\n```lua\n\n local sock = ngx.socket.tcp()\n local ok, err = sock:connect(...)\n if not ok then\n     return nil, err\n end\n return sock\n```\n\nThere is no way to use the [settimeout](#tcpsocksettimeout) method to specify connecting timeout for this method and the [lua_socket_connect_timeout](#lua_socket_connect_timeout) directive must be set at configure time instead.\n\nThis feature was first introduced in the `v0.5.0rc1` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.get_phase\n-------------\n\n**syntax:** *str = ngx.get_phase()*\n\n**context:** *init_by_lua&#42;, init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nRetrieves the current running phase name. Possible return values are\n\n* `init`\n\tfor the context of [init_by_lua*](#init_by_lua).\n* `init_worker`\n\tfor the context of [init_worker_by_lua*](#init_worker_by_lua).\n* `ssl_cert`\n\tfor the context of [ssl_certificate_by_lua*](#ssl_certificate_by_lua_block).\n* `ssl_session_fetch`\n\tfor the context of [ssl_session_fetch_by_lua*](#ssl_session_fetch_by_lua_block).\n* `ssl_session_store`\n\tfor the context of [ssl_session_store_by_lua*](#ssl_session_store_by_lua_block).\n* `ssl_client_hello`\n\tfor the context of [ssl_client_hello_by_lua*](#ssl_client_hello_by_lua_block).\n* `set`\n\tfor the context of [set_by_lua*](#set_by_lua).\n* `rewrite`\n\tfor the context of [rewrite_by_lua*](#rewrite_by_lua).\n* `balancer`\n\tfor the context of [balancer_by_lua*](#balancer_by_lua_block).\n* `access`\n\tfor the context of [access_by_lua*](#access_by_lua).\n* `content`\n\tfor the context of [content_by_lua*](#content_by_lua).\n* `header_filter`\n\tfor the context of [header_filter_by_lua*](#header_filter_by_lua).\n* `body_filter`\n\tfor the context of [body_filter_by_lua*](#body_filter_by_lua).\n* `log`\n\tfor the context of [log_by_lua*](#log_by_lua).\n* `timer`\n\tfor the context of user callback functions for [ngx.timer.*](#ngxtimerat).\n* `exit_worker`\n\tfor the context of [exit_worker_by_lua*](#exit_worker_by_lua).\n\nThis API was first introduced in the `v0.5.10` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.thread.spawn\n----------------\n\n**syntax:** *co = ngx.thread.spawn(func, arg1, arg2, ...)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSpawns 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\".\n\n\"Light threads\" are just a special kind of Lua coroutines that are scheduled by the ngx_lua module.\n\nBefore `ngx.thread.spawn` returns, the `func` will be called with those optional arguments until it returns, aborts with an error, or gets yielded due to I/O operations via the [Nginx API for Lua](#nginx-api-for-lua) (like [tcpsock:receive](#tcpsockreceive)).\n\nAfter `ngx.thread.spawn` returns, the newly-created \"light thread\" will keep running asynchronously usually at various I/O events.\n\nAll the Lua code chunks running by [rewrite_by_lua](#rewrite_by_lua), [access_by_lua](#access_by_lua), and [content_by_lua](#content_by_lua) are in a boilerplate \"light thread\" created automatically by ngx_lua. Such boilerplate \"light thread\" are also called \"entry threads\".\n\nBy default, the corresponding Nginx handler (e.g., [rewrite_by_lua](#rewrite_by_lua) handler) will not terminate until\n\n1. both the \"entry thread\" and all the user \"light threads\" terminates,\n1. a \"light thread\" (either the \"entry thread\" or a user \"light thread\") aborts by calling [ngx.exit](#ngxexit), [ngx.exec](#ngxexec), [ngx.redirect](#ngxredirect), or [ngx.req.set_uri(uri, true)](#ngxreqset_uri), or\n1. the \"entry thread\" terminates with a Lua error.\n\nWhen the user \"light thread\" terminates with a Lua error, however, it will not abort other running \"light threads\" like the \"entry thread\" does.\n\nDue 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`.\n\nThe \"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\n\n1. a (nonblocking) I/O operation cannot be completed in a single run,\n1. it calls [coroutine.yield](#coroutineyield) to actively give up execution, or\n1. it is aborted by a Lua error or an invocation of [ngx.exit](#ngxexit), [ngx.exec](#ngxexec), [ngx.redirect](#ngxredirect), or [ngx.req.set_uri(uri, true)](#ngxreqset_uri).\n\nFor the first two cases, the \"light thread\" will usually be resumed later by the ngx_lua scheduler unless a \"stop-the-world\" event happens.\n\nUser \"light threads\" can create \"light threads\" themselves. And normal user coroutines created by [coroutine.create](#coroutinecreate) can also create \"light threads\". The coroutine (be it a normal Lua coroutine or a \"light thread\") that directly spawns the \"light thread\" is called the \"parent coroutine\" for the \"light thread\" newly spawned.\n\nThe \"parent coroutine\" can call [ngx.thread.wait](#ngxthreadwait) to wait on the termination of its child \"light thread\".\n\nYou can call coroutine.status() and coroutine.yield() on the \"light thread\" coroutines.\n\nThe status of the \"light thread\" coroutine can be \"zombie\" if\n\n1. the current \"light thread\" already terminates (either successfully or with an error),\n1. its parent coroutine is still alive, and\n1. its parent coroutine is not waiting on it with [ngx.thread.wait](#ngxthreadwait).\n\nThe following example demonstrates the use of coroutine.yield() in the \"light thread\" coroutines\nto do manual time-slicing:\n\n```lua\n\n local yield = coroutine.yield\n\n function f()\n     local self = coroutine.running()\n     ngx.say(\"f 1\")\n     yield(self)\n     ngx.say(\"f 2\")\n     yield(self)\n     ngx.say(\"f 3\")\n end\n\n local self = coroutine.running()\n ngx.say(\"0\")\n yield(self)\n\n ngx.say(\"1\")\n ngx.thread.spawn(f)\n\n ngx.say(\"2\")\n yield(self)\n\n ngx.say(\"3\")\n yield(self)\n\n ngx.say(\"4\")\n```\n\nThen it will generate the output\n\n\n    0\n    1\n    f 1\n    2\n    f 2\n    3\n    f 3\n    4\n\n\n\"Light threads\" are mostly useful for making concurrent upstream requests in a single Nginx request handler, much like a generalized version of [ngx.location.capture_multi](#ngxlocationcapture_multi) that can work with all the [Nginx API for Lua](#nginx-api-for-lua). The following example demonstrates parallel requests to MySQL, Memcached, and upstream HTTP services in a single Lua handler, and outputting the results in the order that they actually return (similar to Facebook's BigPipe model):\n\n```lua\n\n -- query mysql, memcached, and a remote http service at the same time,\n -- output the results in the order that they\n -- actually return the results.\n\n local mysql = require \"resty.mysql\"\n local memcached = require \"resty.memcached\"\n\n local function query_mysql()\n     local db = mysql:new()\n     db:connect{\n                 host = \"127.0.0.1\",\n                 port = 3306,\n                 database = \"test\",\n                 user = \"monty\",\n                 password = \"mypass\"\n               }\n     local res, err, errno, sqlstate =\n             db:query(\"select * from cats order by id asc\")\n     db:set_keepalive(0, 100)\n     ngx.say(\"mysql done: \", cjson.encode(res))\n end\n\n local function query_memcached()\n     local memc = memcached:new()\n     memc:connect(\"127.0.0.1\", 11211)\n     local res, err = memc:get(\"some_key\")\n     ngx.say(\"memcached done: \", res)\n end\n\n local function query_http()\n     local res = ngx.location.capture(\"/my-http-proxy\")\n     ngx.say(\"http done: \", res.body)\n end\n\n ngx.thread.spawn(query_mysql)      -- create thread 1\n ngx.thread.spawn(query_memcached)  -- create thread 2\n ngx.thread.spawn(query_http)       -- create thread 3\n```\n\nThis API was first enabled in the `v0.7.0` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.thread.wait\n---------------\n\n**syntax:** *ok, res1, res2, ... = ngx.thread.wait(thread1, thread2, ...)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nWaits on one or more child \"light threads\" and returns the results of the first \"light thread\" that terminates (either successfully or with an error).\n\nThe arguments `thread1`, `thread2`, and etc are the Lua thread objects returned by earlier calls of [ngx.thread.spawn](#ngxthreadspawn).\n\nThe return values have exactly the same meaning as [coroutine.resume](#coroutineresume), that is, the first value returned is a boolean value indicating whether the \"light thread\" terminates successfully or not, and subsequent values returned are the return values of the user Lua function that was used to spawn the \"light thread\" (in case of success) or the error object (in case of failure).\n\nOnly the direct \"parent coroutine\" can wait on its child \"light thread\", otherwise a Lua exception will be raised.\n\nThe following example demonstrates the use of `ngx.thread.wait` and [ngx.location.capture](#ngxlocationcapture) to emulate [ngx.location.capture_multi](#ngxlocationcapture_multi):\n\n```lua\n\n local capture = ngx.location.capture\n local spawn = ngx.thread.spawn\n local wait = ngx.thread.wait\n local say = ngx.say\n\n local function fetch(uri)\n     return capture(uri)\n end\n\n local threads = {\n     spawn(fetch, \"/foo\"),\n     spawn(fetch, \"/bar\"),\n     spawn(fetch, \"/baz\")\n }\n\n for i = 1, #threads do\n     local ok, res = wait(threads[i])\n     if not ok then\n         say(i, \": failed to run: \", res)\n     else\n         say(i, \": status: \", res.status)\n         say(i, \": body: \", res.body)\n     end\n end\n```\n\nHere it essentially implements the \"wait all\" model.\n\nAnd below is an example demonstrating the \"wait any\" model:\n\n```lua\n\n function f()\n     ngx.sleep(0.2)\n     ngx.say(\"f: hello\")\n     return \"f done\"\n end\n\n function g()\n     ngx.sleep(0.1)\n     ngx.say(\"g: hello\")\n     return \"g done\"\n end\n\n local tf, err = ngx.thread.spawn(f)\n if not tf then\n     ngx.say(\"failed to spawn thread f: \", err)\n     return\n end\n\n ngx.say(\"f thread created: \", coroutine.status(tf))\n\n local tg, err = ngx.thread.spawn(g)\n if not tg then\n     ngx.say(\"failed to spawn thread g: \", err)\n     return\n end\n\n ngx.say(\"g thread created: \", coroutine.status(tg))\n\n ok, res = ngx.thread.wait(tf, tg)\n if not ok then\n     ngx.say(\"failed to wait: \", res)\n     return\n end\n\n ngx.say(\"res: \", res)\n\n -- stop the \"world\", aborting other running threads\n ngx.exit(ngx.OK)\n```\n\nAnd it will generate the following output:\n\n\n    f thread created: running\n    g thread created: running\n    g: hello\n    res: g done\n\n\nThis API was first enabled in the `v0.7.0` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.thread.kill\n---------------\n\n**syntax:** *ok, err = ngx.thread.kill(thread)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nKills 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.\n\nAccording to the current implementation, only the parent coroutine (or \"light thread\") can kill a thread. Also, a running \"light thread\" with pending Nginx subrequests (initiated by [ngx.location.capture](#ngxlocationcapture) for example) cannot be killed due to a limitation in the Nginx core.\n\nThis API was first enabled in the `v0.9.9` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.on_abort\n------------\n\n**syntax:** *ok, err = ngx.on_abort(callback)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\nRegisters a user Lua function as the callback which gets called automatically when the client closes the (downstream) connection prematurely.\n\nReturns `1` if the callback is registered successfully or returns `nil` and a string describing the error otherwise.\n\nAll the [Nginx API for Lua](#nginx-api-for-lua) can be used in the callback function because the function is run in a special \"light thread\", just as those \"light threads\" created by [ngx.thread.spawn](#ngxthreadspawn).\n\nThe callback function can decide what to do with the client abortion event all by itself. For example, it can simply ignore the event by doing nothing and the current Lua request handler will continue executing without interruptions. And the callback function can also decide to terminate everything by calling [ngx.exit](#ngxexit), for example,\n\n```lua\n\n local function my_cleanup()\n     -- custom cleanup work goes here, like cancelling a pending DB transaction\n\n     -- now abort all the \"light threads\" running in the current request handler\n     ngx.exit(499)\n end\n\n local ok, err = ngx.on_abort(my_cleanup)\n if not ok then\n     ngx.log(ngx.ERR, \"failed to register the on_abort callback: \", err)\n     ngx.exit(500)\n end\n```\n\nWhen [lua_check_client_abort](#lua_check_client_abort) is set to `off` (which is the default), then this function call will always return the error message \"lua_check_client_abort is off\".\n\nAccording to the current implementation, this function can only be called once in a single request handler; subsequent calls will return the error message \"duplicate call\".\n\nThis API was first introduced in the `v0.7.4` release.\n\nSee also [lua_check_client_abort](#lua_check_client_abort).\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.timer.at\n------------\n\n**syntax:** *hdl, err = ngx.timer.at(delay, callback, user_arg1, user_arg2, ...)*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nCreates an Nginx timer with a user callback function as well as optional user arguments.\n\nThe first argument, `delay`, specifies the delay for the timer,\nin seconds. One can specify fractional seconds like `0.001` to mean 1\nmillisecond here. `0` delay can also be specified, in which case the\ntimer will immediately expire when the current handler yields\nexecution.\n\nThe second argument, `callback`, can\nbe any Lua function, which will be invoked later in a background\n\"light thread\" after the delay specified. The user callback will be\ncalled automatically by the Nginx core with the arguments `premature`,\n`user_arg1`, `user_arg2`, and etc, where the `premature`\nargument takes a boolean value indicating whether it is a premature timer\nexpiration or not(for the `0` delay timer it is always `false`), and `user_arg1`, `user_arg2`, and etc, are\nthose (extra) user arguments specified when calling `ngx.timer.at`\nas the remaining arguments.\n\nPremature timer expiration happens when the Nginx worker process is\ntrying to shut down, as in an Nginx configuration reload triggered by\nthe `HUP` signal or in an Nginx server shutdown. When the Nginx worker\nis trying to shut down, one can no longer call `ngx.timer.at` to\ncreate new timers with nonzero delays and in that case `ngx.timer.at` will return a \"conditional false\" value and\na string describing the error, that is, \"process exiting\".\n\nStarting from the `v0.9.3` release, it is allowed to create zero-delay timers even when the Nginx worker process starts shutting down.\n\nWhen a timer expires, the user Lua code in the timer callback is\nrunning in a \"light thread\" detached completely from the original\nrequest creating the timer. So objects with the same lifetime as the\nrequest creating them, like [cosockets](#ngxsockettcp), cannot be shared between the\noriginal request and the timer user callback function.\n\nHere is a simple example:\n\n```nginx\n\n location / {\n     ...\n     log_by_lua_block {\n         local function push_data(premature, uri, args, status)\n             -- push the data uri, args, and status to the remote\n             -- via ngx.socket.tcp or ngx.socket.udp\n             -- (one may want to buffer the data in Lua a bit to\n             -- save I/O operations)\n         end\n         local ok, err = ngx.timer.at(0, push_data,\n                                      ngx.var.uri, ngx.var.args, ngx.header.status)\n         if not ok then\n             ngx.log(ngx.ERR, \"failed to create timer: \", err)\n             return\n         end\n\n         -- other job in log_by_lua_block\n     }\n }\n```\n\nOne can also create infinite re-occurring timers, for instance, a timer getting triggered every `5` seconds, by calling `ngx.timer.at` recursively in the timer callback function. Here is such an example,\n\n```lua\n\n local delay = 5\n local handler\n handler = function (premature)\n     -- do some routine job in Lua just like a cron job\n     if premature then\n         return\n     end\n     local ok, err = ngx.timer.at(delay, handler)\n     if not ok then\n         ngx.log(ngx.ERR, \"failed to create the timer: \", err)\n         return\n     end\n\n     -- do something in timer\n end\n\n local ok, err = ngx.timer.at(delay, handler)\n if not ok then\n     ngx.log(ngx.ERR, \"failed to create the timer: \", err)\n     return\n end\n\n -- do other jobs\n```\n\nIt is recommended, however, to use the [ngx.timer.every](#ngxtimerevery) API function\ninstead for creating recurring timers since it is more robust.\n\nBecause timer callbacks run in the background and their running time\nwill not add to any client request's response time, they can easily\naccumulate in the server and exhaust system resources due to either\nLua programming mistakes or just too much client traffic. To prevent\nextreme consequences like crashing the Nginx server, there are\nbuilt-in limitations on both the number of \"pending timers\" and the\nnumber of \"running timers\" in an Nginx worker process. The \"pending\ntimers\" here mean timers that have not yet been expired and \"running\ntimers\" are those whose user callbacks are currently running.\n\nThe maximal number of pending timers allowed in an Nginx\nworker is controlled by the [lua_max_pending_timers](#lua_max_pending_timers)\ndirective. The maximal number of running timers is controlled by the\n[lua_max_running_timers](#lua_max_running_timers) directive.\n\nAccording to the current implementation, each \"running timer\" will\ntake one (fake) connection record from the global connection record\nlist configured by the standard [worker_connections](http://nginx.org/en/docs/ngx_core_module.html#worker_connections) directive in\n`nginx.conf`. So ensure that the\n[worker_connections](http://nginx.org/en/docs/ngx_core_module.html#worker_connections) directive is set to\na large enough value that takes into account both the real connections\nand fake connections required by timer callbacks (as limited by the\n[lua_max_running_timers](#lua_max_running_timers) directive).\n\nA lot of the Lua APIs for Nginx are enabled in the context of the timer\ncallbacks, like stream/datagram cosockets ([ngx.socket.tcp](#ngxsockettcp) and [ngx.socket.udp](#ngxsocketudp)), shared\nmemory dictionaries ([ngx.shared.DICT](#ngxshareddict)), user coroutines ([coroutine.*](#coroutinecreate)),\nuser \"light threads\" ([ngx.thread.*](#ngxthreadspawn)), [ngx.exit](#ngxexit), [ngx.now](#ngxnow)/[ngx.time](#ngxtime),\n[ngx.md5](#ngxmd5)/[ngx.sha1_bin](#ngxsha1_bin), are all allowed. But the subrequest API (like\n[ngx.location.capture](#ngxlocationcapture)), the [ngx.req.*](#ngxreqstart_time) API, the downstream output API\n(like [ngx.say](#ngxsay), [ngx.print](#ngxprint), and [ngx.flush](#ngxflush)) are explicitly disabled in\nthis context.\n\nYou 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.\n\nYou 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.\n\nPlease note that the timer Lua handler has its own copy of the `ngx.ctx` magic\ntable. It won't share the same `ngx.ctx` with the Lua handler creating the timer.\nIf you need to pass data from the timer creator to the timer handler, please\nuse the extra parameters of `ngx.timer.at()`.\n\nThis API was first introduced in the `v0.8.0` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.timer.every\n---------------\n\n**syntax:** *hdl, err = ngx.timer.every(delay, callback, user_arg1, user_arg2, ...)*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSimilar to the [ngx.timer.at](#ngxtimerat) API function, but\n\n1. `delay` *cannot* be zero,\n1. timer will be created every `delay` seconds until the current Nginx worker process starts exiting.\n\nLike [ngx.timer.at](#ngxtimerat), the `callback` argument will be called\nautomatically with the arguments `premature`, `user_arg1`, `user_arg2`, etc.\n\nWhen success, returns a \"conditional true\" value (but not a `true`). Otherwise, returns a \"conditional false\" value and a string describing the error.\n\nThis API also respect the [lua_max_pending_timers](#lua_max_pending_timers) and [lua_max_running_timers](#lua_max_running_timers).\n\nThis API was first introduced in the `v0.10.9` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.timer.running_count\n-----------------------\n\n**syntax:** *count = ngx.timer.running_count()*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns the number of timers currently running.\n\nThis directive was first introduced in the `v0.9.20` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.timer.pending_count\n-----------------------\n\n**syntax:** *count = ngx.timer.pending_count()*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nReturns the number of pending timers.\n\nThis directive was first introduced in the `v0.9.20` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.config.subsystem\n--------------------\n\n**syntax:** *subsystem = ngx.config.subsystem*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, init_by_lua&#42;, init_worker_by_lua&#42;, exit_worker_by_lua&#42;*\n\nThis string field indicates the Nginx subsystem the current Lua environment is based on. For this module, this field always takes the string value `\"http\"`. For\n[ngx_stream_lua_module](https://github.com/openresty/stream-lua-nginx-module#readme), however, this field takes the value `\"stream\"`.\n\nThis field was first introduced in the `0.10.1`.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.config.debug\n----------------\n\n**syntax:** *debug = ngx.config.debug*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, init_by_lua&#42;, init_worker_by_lua&#42;, exit_worker_by_lua&#42;*\n\nThis boolean field indicates whether the current Nginx is a debug build, i.e., being built by the `./configure` option `--with-debug`.\n\nThis field was first introduced in the `0.8.7`.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.config.prefix\n-----------------\n\n**syntax:** *prefix = ngx.config.prefix()*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, init_by_lua&#42;, init_worker_by_lua&#42;, exit_worker_by_lua&#42;*\n\nReturns the Nginx server \"prefix\" path, as determined by the `-p` command-line option when running the Nginx executable, or the path specified by the `--prefix` command-line option when building Nginx with the `./configure` script.\n\nThis function was first introduced in the `0.9.2`.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.config.nginx_version\n------------------------\n\n**syntax:** *ver = ngx.config.nginx_version*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, init_by_lua&#42;, init_worker_by_lua&#42;, exit_worker_by_lua&#42;*\n\nThis field take an integral value indicating the version number of the current Nginx core being used. For example, the version number `1.4.3` results in the Lua number 1004003.\n\nThis API was first introduced in the `0.9.3` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.config.nginx_configure\n--------------------------\n\n**syntax:** *str = ngx.config.nginx_configure()*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, init_by_lua&#42;*\n\nThis function returns a string for the Nginx `./configure` command's arguments string.\n\nThis API was first introduced in the `0.9.5` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.config.ngx_lua_version\n--------------------------\n\n**syntax:** *ver = ngx.config.ngx_lua_version*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, init_by_lua&#42;*\n\nThis field take an integral value indicating the version number of the current `ngx_lua` module being used. For example, the version number `0.9.3` results in the Lua number 9003.\n\nThis API was first introduced in the `0.9.3` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.worker.exiting\n------------------\n\n**syntax:** *exiting = ngx.worker.exiting()*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, init_by_lua&#42;, init_worker_by_lua&#42;, exit_worker_by_lua&#42;*\n\nThis function returns a boolean value indicating whether the current Nginx worker process already starts exiting. Nginx worker process exiting happens on Nginx server quit or configuration reload (aka HUP reload).\n\nThis API was first introduced in the `0.9.3` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.worker.pid\n--------------\n\n**syntax:** *pid = ngx.worker.pid()*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, init_by_lua&#42;, init_worker_by_lua&#42;, exit_worker_by_lua&#42;*\n\nThis function returns a Lua number for the process ID (PID) of the current Nginx worker process. This API is more efficient than `ngx.var.pid` and can be used in contexts where the [ngx.var.VARIABLE](#ngxvarvariable) API cannot be used (like [init_worker_by_lua](#init_worker_by_lua)).\n\nThis API was first introduced in the `0.9.5` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.worker.pids\n--------------\n\n**syntax:** *pids = ngx.worker.pids()*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, exit_worker_by_lua&#42;*\n\nThis 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.\n\nThis API was first introduced in the `0.10.23` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.worker.count\n----------------\n\n**syntax:** *count = ngx.worker.count()*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, init_by_lua&#42;, init_worker_by_lua&#42;, exit_worker_by_lua&#42;*\n\nReturns the total number of the Nginx worker processes (i.e., the value configured\nby the [worker_processes](https://nginx.org/en/docs/ngx_core_module.html#worker_processes)\ndirective in `nginx.conf`).\n\nThis API was first introduced in the `0.9.20` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.worker.id\n-------------\n\n**syntax:** *id = ngx.worker.id()*\n\n**context:** *set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, init_worker_by_lua&#42;, exit_worker_by_lua&#42;*\n\nReturns the ordinal number of the current Nginx worker processes (starting from number 0).\n\nSo if the total number of workers is `N`, then this method may return a number between 0\nand `N - 1` (inclusive).\n\nThis function returns meaningful values only for Nginx 1.9.1+. With earlier versions of Nginx, it\nalways returns `nil`.\n\nSee also [ngx.worker.count](#ngxworkercount).\n\nThis API was first introduced in the `0.9.20` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.semaphore\n-------------\n\n**syntax:** *local semaphore = require \"ngx.semaphore\"*\n\nThis is a Lua module that implements a classic-style semaphore API for efficient synchronizations among\ndifferent \"light threads\". Sharing the same semaphore among different \"light threads\" created in different (request)\ncontexts are also supported as long as the \"light threads\" reside in the same Nginx worker process\nand the [lua_code_cache](#lua_code_cache) directive is turned on (which is the default).\n\nThis Lua module does not ship with this ngx_lua module itself rather it is shipped with\nthe\n[lua-resty-core](https://github.com/openresty/lua-resty-core) library.\n\nPlease refer to the [documentation](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/semaphore.md)\nfor this `ngx.semaphore` Lua module in [lua-resty-core](https://github.com/openresty/lua-resty-core)\nfor more details.\n\nThis feature requires at least ngx_lua `v0.10.0`.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.balancer\n------------\n\n**syntax:** *local balancer = require \"ngx.balancer\"*\n\nThis is a Lua module that provides a Lua API to allow defining completely dynamic load balancers\nin pure Lua.\n\nThis Lua module does not ship with this ngx_lua module itself rather it is shipped with\nthe\n[lua-resty-core](https://github.com/openresty/lua-resty-core) library.\n\nPlease refer to the [documentation](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/balancer.md)\nfor this `ngx.balancer` Lua module in [lua-resty-core](https://github.com/openresty/lua-resty-core)\nfor more details.\n\nThis feature requires at least ngx_lua `v0.10.0`.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.ssl\n-------\n\n**syntax:** *local ssl = require \"ngx.ssl\"*\n\nThis Lua module provides API functions to control the SSL handshake process in contexts like\n[ssl_certificate_by_lua*](#ssl_certificate_by_lua_block).\n\nThis Lua module does not ship with this ngx_lua module itself rather it is shipped with\nthe\n[lua-resty-core](https://github.com/openresty/lua-resty-core) library.\n\nPlease refer to the [documentation](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md)\nfor this `ngx.ssl` Lua module for more details.\n\nThis feature requires at least ngx_lua `v0.10.0`.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.ocsp\n--------\n\n**syntax:** *local ocsp = require \"ngx.ocsp\"*\n\nThis Lua module provides API to perform OCSP queries, OCSP response validations, and\nOCSP stapling planting.\n\nUsually, this module is used together with the [ngx.ssl](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md)\nmodule in the\ncontext of [ssl_certificate_by_lua*](#ssl_certificate_by_lua_block).\n\nThis Lua module does not ship with this ngx_lua module itself rather it is shipped with\nthe\n[lua-resty-core](https://github.com/openresty/lua-resty-core) library.\n\nPlease refer to the [documentation](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ocsp.md)\nfor this `ngx.ocsp` Lua module for more details.\n\nThis feature requires at least ngx_lua `v0.10.0`.\n\n[Back to TOC](#nginx-api-for-lua)\n\nndk.set_var.DIRECTIVE\n---------------------\n\n**syntax:** *res = ndk.set_var.DIRECTIVE_NAME*\n\n**context:** *init_worker_by_lua&#42;, set_by_lua&#42;, rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, log_by_lua&#42;, ngx.timer.&#42;, balancer_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, exit_worker_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nThis 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`.\n\nFor example, the following [set-misc-nginx-module](http://github.com/openresty/set-misc-nginx-module) directives can be invoked this way:\n\n* [set_quote_sql_str](http://github.com/openresty/set-misc-nginx-module#set_quote_sql_str)\n* [set_quote_pgsql_str](http://github.com/openresty/set-misc-nginx-module#set_quote_pgsql_str)\n* [set_quote_json_str](http://github.com/openresty/set-misc-nginx-module#set_quote_json_str)\n* [set_unescape_uri](http://github.com/openresty/set-misc-nginx-module#set_unescape_uri)\n* [set_escape_uri](http://github.com/openresty/set-misc-nginx-module#set_escape_uri)\n* [set_encode_base32](http://github.com/openresty/set-misc-nginx-module#set_encode_base32)\n* [set_decode_base32](http://github.com/openresty/set-misc-nginx-module#set_decode_base32)\n* [set_encode_base64](http://github.com/openresty/set-misc-nginx-module#set_encode_base64)\n* [set_decode_base64](http://github.com/openresty/set-misc-nginx-module#set_decode_base64)\n* [set_encode_hex](http://github.com/openresty/set-misc-nginx-module#set_encode_base64)\n* [set_decode_hex](http://github.com/openresty/set-misc-nginx-module#set_decode_base64)\n* [set_sha1](http://github.com/openresty/set-misc-nginx-module#set_encode_base64)\n* [set_md5](http://github.com/openresty/set-misc-nginx-module#set_decode_base64)\n\nFor instance,\n\n```lua\n\n local res = ndk.set_var.set_escape_uri('a/b')\n -- now res == 'a%2fb'\n```\n\nSimilarly, the following directives provided by [encrypted-session-nginx-module](http://github.com/openresty/encrypted-session-nginx-module) can be invoked from within Lua too:\n\n* [set_encrypt_session](http://github.com/openresty/encrypted-session-nginx-module#set_encrypt_session)\n* [set_decrypt_session](http://github.com/openresty/encrypted-session-nginx-module#set_decrypt_session)\n\nThis feature requires the [ngx_devel_kit](https://github.com/simplresty/ngx_devel_kit) module.\n\n[Back to TOC](#nginx-api-for-lua)\n\ncoroutine.create\n----------------\n\n**syntax:** *co = coroutine.create(f)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, init_by_lua&#42;, ngx.timer.&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nCreates a user Lua coroutines with a Lua function, and returns a coroutine object.\n\nSimilar to the standard Lua [coroutine.create](https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.create) API, but works in the context of the Lua coroutines created by ngx_lua.\n\nThis API was first usable in the context of [init_by_lua*](#init_by_lua) since the `0.9.2`.\n\nThis API was first introduced in the `v0.6.0` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ncoroutine.resume\n----------------\n\n**syntax:** *ok, ... = coroutine.resume(co, ...)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, init_by_lua&#42;, ngx.timer.&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nResumes the execution of a user Lua coroutine object previously yielded or just created.\n\nSimilar 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.\n\nThis API was first usable in the context of [init_by_lua*](#init_by_lua) since the `0.9.2`.\n\nThis API was first introduced in the `v0.6.0` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ncoroutine.yield\n---------------\n\n**syntax:** *... = coroutine.yield(...)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, init_by_lua&#42;, ngx.timer.&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nYields the execution of the current user Lua coroutine.\n\nSimilar to the standard Lua [coroutine.yield](https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.yield) API, but works in the context of the Lua coroutines created by ngx_lua.\n\nThis API was first usable in the context of [init_by_lua*](#init_by_lua) since the `0.9.2`.\n\nThis API was first introduced in the `v0.6.0` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ncoroutine.wrap\n--------------\n\n**syntax:** *co = coroutine.wrap(f)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, init_by_lua&#42;, ngx.timer.&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nSimilar 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.\n\nThis API was first usable in the context of [init_by_lua*](#init_by_lua) since the `0.9.2`.\n\nThis API was first introduced in the `v0.6.0` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ncoroutine.running\n-----------------\n\n**syntax:** *co = coroutine.running()*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, init_by_lua&#42;, ngx.timer.&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nIdentical to the standard Lua [coroutine.running](https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.running) API.\n\nThis API was first usable in the context of [init_by_lua*](#init_by_lua) since the `0.9.2`.\n\nThis API was first enabled in the `v0.6.0` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\ncoroutine.status\n----------------\n\n**syntax:** *status = coroutine.status(co)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, init_by_lua&#42;, ngx.timer.&#42;, header_filter_by_lua&#42;, body_filter_by_lua&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_session_store_by_lua&#42;, ssl_client_hello_by_lua&#42;*\n\nIdentical to the standard Lua [coroutine.status](https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.status) API.\n\nThis API was first usable in the context of [init_by_lua*](#init_by_lua) since the `0.9.2`.\n\nThis API was first enabled in the `v0.6.0` release.\n\n[Back to TOC](#nginx-api-for-lua)\n\nngx.run_worker_thread\n---------------------\n\n**syntax:** *ok, res1, res2, ... = ngx.run_worker_thread(threadpool, module_name, func_name, arg1, arg2, ...)*\n\n**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;*\n\n**This API is still experimental and may change in the future without notice.**\n\n**This API is available only for Linux.**\n\nWrap 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.\n\nOnly the following ngx_lua APIs could be used in `function_name` function of the `module` module:\n\n* `ngx.encode_base64`\n* `ngx.decode_base64`\n\n* `ngx.hmac_sha1`\n* `ngx.encode_args`\n* `ngx.decode_args`\n* `ngx.quote_sql_str`\n\n* `ngx.crc32_short`\n* `ngx.crc32_long`\n* `ngx.hmac_sha1`\n* `ngx.md5_bin`\n* `ngx.md5`\n\n* `ngx.config.subsystem`\n* `ngx.config.debug`\n* `ngx.config.prefix`\n* `ngx.config.nginx_version`\n* `ngx.config.nginx_configure`\n* `ngx.config.ngx_lua_version`\n\n* `ngx.shared.DICT`\n\nThe first argument `threadpool` specifies the Nginx thread pool name defined by [thread_pool](https://nginx.org/en/docs/ngx_core_module.html#thread_pool).\n\nThe 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.\n\n```nginx\n\n lua_package_path '/opt/openresty/?.lua;;';\n```\n\nThe third argument `func_name` specifies the function field in the module table as the second argument.\n\nThe type of `args` must be one of type below:\n\n* boolean\n* number\n* string\n* nil\n* table (the table may be recursive, and contains members of types above.)\n\nThe `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.\n\nThe 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.\n\nThis API is useful when you need to execute the below types of tasks:\n\n* CPU bound task, e.g. do md5 calculation\n* File I/O task\n* Call `os.execute()` or blocking C API via `ffi`\n* Call external Lua library not based on cosocket or nginx\n\nExample1: do md5 calculation.\n\n```nginx\n\n location /calc_md5 {\n     default_type 'text/plain';\n\n     content_by_lua_block {\n         local ok, md5_or_err = ngx.run_worker_thread(\"testpool\", \"md5\", \"md5\")\n         ngx.say(ok, \" : \", md5_or_err)\n     }\n }\n```\n\n`md5.lua`\n\n```lua\nlocal function md5()\n    return ngx.md5(\"hello\")\nend\n\nreturn { md5=md5, }\n```\n\nExample2: write logs into the log file.\n\n```nginx\n\n location /write_log_file {\n     default_type 'text/plain';\n\n     content_by_lua_block {\n         local ok, err = ngx.run_worker_thread(\"testpool\", \"write_log_file\", \"log\", ngx.var.arg_str)\n         if not ok then\n             ngx.say(ok, \" : \", err)\n             return\n         end\n         ngx.say(ok)\n     }\n }\n```\n\n`write_log_file.lua`\n\n```lua\n\n local function log(str)\n     local file, err = io.open(\"/tmp/tmp.log\", \"a\")\n     if not file then\n         return false, err\n     end\n     file:write(str)\n     file:flush()\n     file:close()\n     return true\n end\n return {log=log}\n```\n\n[Back to TOC](#nginx-api-for-lua)\n\nObsolete Sections\n=================\n\nThis section is just holding obsolete documentation sections that have been either renamed or removed so that existing links over the web are still valid.\n\n[Back to TOC](#table-of-contents)\n\nSpecial PCRE Sequences\n----------------------\n\nThis section has been renamed to [Special Escaping Sequences](#special-escaping-sequences).\n\n[Back to TOC](#table-of-contents)\n\nLua/LuaJIT bytecode support\n---------------------------\n\nThis section has been renamed to\n[LuaJIT bytecode support](#luajit-bytecode-support). As of version\n`v0.10.16` of this module, the standard Lua interpreter (also known\nas \"PUC-Rio Lua\") is not supported anymore.\n"
  },
  {
    "path": "config",
    "content": "ngx_lua_opt_I=\nngx_lua_opt_L=\nluajit_ld_opt=\n\nngx_feature_name=\nngx_feature_run=no\nngx_feature_incs=\nngx_feature_test=\n\nif [ -n \"$LUAJIT_INC\" -o -n \"$LUAJIT_LIB\" ]; then\n    # explicitly set LuaJIT paths\n\n    if [ \"$NGX_PLATFORM\" = win32 ]; then\n        ngx_feature=\"LuaJIT library in $LUAJIT_LIB and $LUAJIT_INC (win32)\"\n        ngx_feature_path=\"$LUAJIT_INC\"\n        ngx_lua_opt_I=\"-I$LUAJIT_INC\"\n        ngx_lua_opt_L=\"-L$LUAJIT_LIB\"\n\n        # ensure that -I$LUAJIT_INC and -L$LUAJIT_LIB come first\n        SAVED_CC_TEST_FLAGS=\"$CC_TEST_FLAGS\"\n        CC_TEST_FLAGS=\"$ngx_lua_opt_I $CC_TEST_FLAGS\"\n        SAVED_NGX_TEST_LD_OPT=\"$NGX_TEST_LD_OPT\"\n        NGX_TEST_LD_OPT=\"$ngx_lua_opt_L $NGX_TEST_LD_OPT\"\n\n        # LuaJIT's win32 build uses the library file name lua51.dll.\n        ngx_feature_libs=\"$ngx_lua_opt_L -llua51\"\n\n        . auto/feature\n\n        # clean up\n        CC_TEST_FLAGS=\"$SAVED_CC_TEST_FLAGS\"\n        NGX_TEST_LD_OPT=\"$SAVED_NGX_TEST_LD_OPT\"\n    else\n        # attempt to link with -ldl, static linking on Linux requires it.\n        ngx_feature=\"LuaJIT library in $LUAJIT_LIB and $LUAJIT_INC (specified by the LUAJIT_LIB and LUAJIT_INC env, with -ldl)\"\n        ngx_feature_path=\"$LUAJIT_INC\"\n        ngx_lua_opt_I=\"-I$LUAJIT_INC\"\n        ngx_lua_opt_L=\"-L$LUAJIT_LIB\"\n        luajit_ld_opt=\"-lm -ldl\"\n\n        # ensure that -I$LUAJIT_INC and -L$LUAJIT_LIB come first\n        SAVED_CC_TEST_FLAGS=\"$CC_TEST_FLAGS\"\n        CC_TEST_FLAGS=\"$ngx_lua_opt_I $CC_TEST_FLAGS\"\n        SAVED_NGX_TEST_LD_OPT=\"$NGX_TEST_LD_OPT\"\n        NGX_TEST_LD_OPT=\"$ngx_lua_opt_L $NGX_TEST_LD_OPT\"\n\n        if [ $NGX_RPATH = YES ]; then\n            ngx_feature_libs=\"-R$LUAJIT_LIB $ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt\"\n        else\n            ngx_feature_libs=\"$ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt\"\n        fi\n\n        . auto/feature\n\n        # clean up\n        CC_TEST_FLAGS=\"$SAVED_CC_TEST_FLAGS\"\n        NGX_TEST_LD_OPT=\"$SAVED_NGX_TEST_LD_OPT\"\n\n        if [ $ngx_found = no ]; then\n            # retry without -ldl\n            ngx_feature=\"LuaJIT library in $LUAJIT_LIB and $LUAJIT_INC (specified by the LUAJIT_LIB and LUAJIT_INC env)\"\n            ngx_feature_path=\"$LUAJIT_INC\"\n            ngx_lua_opt_I=\"-I$LUAJIT_INC\"\n            ngx_lua_opt_L=\"-L$LUAJIT_LIB\"\n            luajit_ld_opt=\"-lm\"\n\n            # ensure that -I$LUAJIT_INC and -L$LUAJIT_LIB come first\n            SAVED_CC_TEST_FLAGS=\"$CC_TEST_FLAGS\"\n            CC_TEST_FLAGS=\"$ngx_lua_opt_I $CC_TEST_FLAGS\"\n            SAVED_NGX_TEST_LD_OPT=\"$NGX_TEST_LD_OPT\"\n            NGX_TEST_LD_OPT=\"$ngx_lua_opt_L $NGX_TEST_LD_OPT\"\n\n            if [ $NGX_RPATH = YES ]; then\n                ngx_feature_libs=\"-R$LUAJIT_LIB $ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt\"\n            else\n                ngx_feature_libs=\"$ngx_lua_opt_L -lluajit-5.1 $luajit_ld_opt\"\n            fi\n\n            . auto/feature\n\n            # clean up\n            CC_TEST_FLAGS=\"$SAVED_CC_TEST_FLAGS\"\n            NGX_TEST_LD_OPT=\"$SAVED_NGX_TEST_LD_OPT\"\n        fi\n    fi\n\n    if [ $ngx_found = no ]; then\n        cat << END\n        $0: error: ngx_http_lua_module requires the LuaJIT library, but it could not be found where specified (LUAJIT_LIB=$LUAJIT_LIB, LUAJIT_INC=$LUAJIT_INC).\nEND\n        exit 1\n    fi\n\n    case \"$NGX_PLATFORM\" in\n        Darwin:*)\n            case \"$NGX_MACHINE\" in\n                amd64 | x86_64 | i386)\n                    echo \"adding extra linking options needed by LuaJIT on $NGX_MACHINE\"\n                    luajit_ld_opt=\"$luajit_ld_opt -pagezero_size 10000 -image_base 100000000\"\n                    ngx_feature_libs=\"$ngx_feature_libs -pagezero_size 10000 -image_base 100000000\"\n                ;;\n\n                *)\n                ;;\n            esac\n        ;;\n\n        *)\n        ;;\n    esac\nelse\n    # auto-discovery\n    if [ $ngx_found = no ]; then\n        # FreeBSD with luajit-2.0 from ports collection\n        ngx_feature=\"LuaJIT library in /usr/local/\"\n        ngx_feature_path=\"/usr/local/include/luajit-2.0\"\n        luajit_ld_opt=\"-lm\"\n        LUAJIT_LIB=\"/usr/local/lib\"\n        if [ $NGX_RPATH = YES ]; then\n            ngx_feature_libs=\"-R/usr/local/lib -L/usr/local/lib -lluajit-5.1 -lm\"\n        else\n            ngx_feature_libs=\"-L/usr/local/lib -lluajit-5.1 -lm\"\n        fi\n        . auto/feature\n    fi\n\n    if [ $ngx_found = no ]; then\n        # Gentoo with LuaJIT-2.0, try with -ldl\n        ngx_feature=\"LuaJIT library in /usr/\"\n        ngx_feature_path=\"/usr/include/luajit-2.0\"\n        luajit_ld_opt=\"-lm -ldl\"\n        LUAJIT_LIB=\"/usr/lib\"\n        if [ $NGX_RPATH = YES ]; then\n            ngx_feature_libs=\"-R/usr/lib -L/usr/lib -lm -lluajit-5.1 -ldl\"\n        else\n            ngx_feature_libs=\"-L/usr/lib -lm -lluajit-5.1 -ldl\"\n        fi\n        . auto/feature\n    fi\n\n    if [ $ngx_found = no ]; then\n        # Gentoo with LuaJIT 2.0\n        ngx_feature=\"LuaJIT library in /usr/\"\n        ngx_feature_path=\"/usr/include/luajit-2.0\"\n        luajit_ld_opt=\"-lm\"\n        LUAJIT_LIB=\"/usr/lib\"\n        if [ $NGX_RPATH = YES ]; then\n            ngx_feature_libs=\"-R/usr/lib -L/usr/lib -lm -lluajit-5.1\"\n        else\n            ngx_feature_libs=\"-L/usr/lib -lm -lluajit-5.1\"\n        fi\n        . auto/feature\n    fi\nfi\n\nngx_module_incs=\nngx_module_libs=\n\nif [ $ngx_found = yes ]; then\n    # this is a hack to persuade nginx's build system to favor\n    # the paths set by our user environment\n    CFLAGS=\"$ngx_lua_opt_I $CFLAGS\"\n    NGX_LD_OPT=\"$ngx_lua_opt_L $NGX_LD_OPT\"\n\n    ngx_module_incs=\"$ngx_module_incs $ngx_feature_path\"\n    ngx_module_libs=\"$ngx_module_libs $ngx_feature_libs\"\nelse\n    cat << END\n    $0: error: ngx_http_lua_module requires the LuaJIT library.\nEND\n    exit 1\nfi\n\n# ----------------------------------------\n\nngx_feature=\"LuaJIT 2.x\"\nngx_feature_run=no\nngx_feature_incs=\"#include <luajit.h>\"\nngx_feature_test=\"#if !defined(LUAJIT_VERSION_NUM) || LUAJIT_VERSION_NUM < 20000\n                  #    error unsupported LuaJIT version\n                  #endif\n                  \"\n\n. auto/feature\n\nif [ $ngx_found = no ]; then\n    cat << END\n    $0: error: unsupported LuaJIT version; ngx_http_lua_module requires LuaJIT 2.x.\nEND\n    exit 1\nfi\n\n# ----------------------------------------\n\nngx_feature=\"Lua language 5.1\"\nngx_feature_run=no\nngx_feature_incs=\"#include <lua.h>\"\nngx_feature_test=\"#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM != 501\n                  #   error unsupported Lua language version\n                  #endif\n                  \"\n\n. auto/feature\n\nif [ $ngx_found = no ]; then\n    cat << END\n    $0: error: unsupported Lua language version; ngx_http_lua_module requires Lua 5.1.\nEND\n    exit 1\nfi\n\n# ----------------------------------------\n\nngx_feature=\"LuaJIT has FFI\"\nngx_feature_libs=\"$ngx_module_libs\"\nngx_feature_run=no\nngx_feature_incs=\"#include <lualib.h>\n                  #include <lauxlib.h>\n                  #include <assert.h>\n                  \"\nngx_feature_test=\"lua_State *L = luaL_newstate();\n                  assert(L != NULL);\n                  luaopen_ffi(L);\n                  \"\n\n. auto/feature\n\nif [ $ngx_found = no ]; then\n    cat << END\n    $0: error: unsupported LuaJIT build; ngx_http_lua_module requires LuaJIT with FFI enabled.\nEND\n    exit 1\nfi\n\n# ----------------------------------------\n\nngx_addon_name=ngx_http_lua_module\nHTTP_LUA_SRCS=\" \\\n            $ngx_addon_dir/src/ngx_http_lua_script.c \\\n            $ngx_addon_dir/src/ngx_http_lua_log.c \\\n            $ngx_addon_dir/src/ngx_http_lua_subrequest.c \\\n            $ngx_addon_dir/src/ngx_http_lua_ndk.c \\\n            $ngx_addon_dir/src/ngx_http_lua_control.c \\\n            $ngx_addon_dir/src/ngx_http_lua_time.c \\\n            $ngx_addon_dir/src/ngx_http_lua_misc.c \\\n            $ngx_addon_dir/src/ngx_http_lua_variable.c \\\n            $ngx_addon_dir/src/ngx_http_lua_string.c \\\n            $ngx_addon_dir/src/ngx_http_lua_output.c \\\n            $ngx_addon_dir/src/ngx_http_lua_headers.c \\\n            $ngx_addon_dir/src/ngx_http_lua_req_body.c \\\n            $ngx_addon_dir/src/ngx_http_lua_uri.c \\\n            $ngx_addon_dir/src/ngx_http_lua_args.c \\\n            $ngx_addon_dir/src/ngx_http_lua_ctx.c \\\n            $ngx_addon_dir/src/ngx_http_lua_regex.c \\\n            $ngx_addon_dir/src/ngx_http_lua_module.c \\\n            $ngx_addon_dir/src/ngx_http_lua_headers_out.c \\\n            $ngx_addon_dir/src/ngx_http_lua_headers_in.c \\\n            $ngx_addon_dir/src/ngx_http_lua_directive.c \\\n            $ngx_addon_dir/src/ngx_http_lua_consts.c \\\n            $ngx_addon_dir/src/ngx_http_lua_exception.c \\\n            $ngx_addon_dir/src/ngx_http_lua_util.c \\\n            $ngx_addon_dir/src/ngx_http_lua_cache.c \\\n            $ngx_addon_dir/src/ngx_http_lua_precontentby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_contentby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_server_rewriteby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_rewriteby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_accessby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_setby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_capturefilter.c \\\n            $ngx_addon_dir/src/ngx_http_lua_clfactory.c \\\n            $ngx_addon_dir/src/ngx_http_lua_pcrefix.c \\\n            $ngx_addon_dir/src/ngx_http_lua_headerfilterby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_shdict.c \\\n            $ngx_addon_dir/src/ngx_http_lua_socket_tcp.c \\\n            $ngx_addon_dir/src/ngx_http_lua_api.c \\\n            $ngx_addon_dir/src/ngx_http_lua_logby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_sleep.c \\\n            $ngx_addon_dir/src/ngx_http_lua_semaphore.c\\\n            $ngx_addon_dir/src/ngx_http_lua_coroutine.c \\\n            $ngx_addon_dir/src/ngx_http_lua_bodyfilterby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_initby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_initworkerby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_exitworkerby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_socket_udp.c \\\n            $ngx_addon_dir/src/ngx_http_lua_req_method.c \\\n            $ngx_addon_dir/src/ngx_http_lua_phase.c \\\n            $ngx_addon_dir/src/ngx_http_lua_uthread.c \\\n            $ngx_addon_dir/src/ngx_http_lua_timer.c \\\n            $ngx_addon_dir/src/ngx_http_lua_config.c \\\n            $ngx_addon_dir/src/ngx_http_lua_worker.c \\\n            $ngx_addon_dir/src/ngx_http_lua_ssl_client_helloby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_ssl_certby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_ssl_export_keying_material.c \\\n            $ngx_addon_dir/src/ngx_http_lua_ssl_ocsp.c \\\n            $ngx_addon_dir/src/ngx_http_lua_lex.c \\\n            $ngx_addon_dir/src/ngx_http_lua_balancer.c \\\n            $ngx_addon_dir/src/ngx_http_lua_ssl_session_storeby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_ssl_session_fetchby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_ssl.c \\\n            $ngx_addon_dir/src/ngx_http_lua_proxy_ssl_certby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_proxy_ssl_verifyby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_log_ringbuf.c \\\n            $ngx_addon_dir/src/ngx_http_lua_input_filters.c \\\n            $ngx_addon_dir/src/ngx_http_lua_pipe.c \\\n            $ngx_addon_dir/src/ngx_http_lua_worker_thread.c \\\n            \"\n\nHTTP_LUA_DEPS=\" \\\n            $ngx_addon_dir/src/ddebug.h \\\n            $ngx_addon_dir/src/ngx_http_lua_autoconf.h \\\n            $ngx_addon_dir/src/ngx_http_lua_script.h \\\n            $ngx_addon_dir/src/ngx_http_lua_log.h \\\n            $ngx_addon_dir/src/ngx_http_lua_subrequest.h \\\n            $ngx_addon_dir/src/ngx_http_lua_ndk.h \\\n            $ngx_addon_dir/src/ngx_http_lua_control.h \\\n            $ngx_addon_dir/src/ngx_http_lua_string.h \\\n            $ngx_addon_dir/src/ngx_http_lua_misc.h \\\n            $ngx_addon_dir/src/ngx_http_lua_output.h \\\n            $ngx_addon_dir/src/ngx_http_lua_headers.h \\\n            $ngx_addon_dir/src/ngx_http_lua_uri.h \\\n            $ngx_addon_dir/src/ngx_http_lua_req_body.h \\\n            $ngx_addon_dir/src/ngx_http_lua_args.h \\\n            $ngx_addon_dir/src/ngx_http_lua_ctx.h \\\n            $ngx_addon_dir/src/ngx_http_lua_common.h \\\n            $ngx_addon_dir/src/ngx_http_lua_directive.h \\\n            $ngx_addon_dir/src/ngx_http_lua_headers_out.h \\\n            $ngx_addon_dir/src/ngx_http_lua_headers_in.h \\\n            $ngx_addon_dir/src/ngx_http_lua_consts.h \\\n            $ngx_addon_dir/src/ngx_http_lua_exception.h \\\n            $ngx_addon_dir/src/ngx_http_lua_util.h \\\n            $ngx_addon_dir/src/ngx_http_lua_cache.h \\\n            $ngx_addon_dir/src/ngx_http_lua_precontentby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_contentby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_server_rewriteby.c \\\n            $ngx_addon_dir/src/ngx_http_lua_rewriteby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_accessby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_setby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_capturefilter.h \\\n            $ngx_addon_dir/src/ngx_http_lua_clfactory.h \\\n            $ngx_addon_dir/src/ngx_http_lua_pcrefix.h \\\n            $ngx_addon_dir/src/ngx_http_lua_headerfilterby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_shdict.h \\\n            $ngx_addon_dir/src/ngx_http_lua_socket_tcp.h \\\n            $ngx_addon_dir/src/api/ngx_http_lua_api.h \\\n            $ngx_addon_dir/src/ngx_http_lua_logby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_sleep.h \\\n            $ngx_addon_dir/src/ngx_http_lua_semaphore.h\\\n            $ngx_addon_dir/src/ngx_http_lua_coroutine.h \\\n            $ngx_addon_dir/src/ngx_http_lua_bodyfilterby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_initby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_initworkerby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_exitworkerby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_socket_udp.h \\\n            $ngx_addon_dir/src/ngx_http_lua_probe.h \\\n            $ngx_addon_dir/src/ngx_http_lua_uthread.h \\\n            $ngx_addon_dir/src/ngx_http_lua_timer.h \\\n            $ngx_addon_dir/src/ngx_http_lua_config.h \\\n            $ngx_addon_dir/src/ngx_http_lua_ssl_client_helloby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_ssl_certby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_lex.h \\\n            $ngx_addon_dir/src/ngx_http_lua_balancer.h \\\n            $ngx_addon_dir/src/ngx_http_lua_ssl_export_keying_material.h \\\n            $ngx_addon_dir/src/ngx_http_lua_ssl_session_storeby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_ssl_session_fetchby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_ssl.h \\\n            $ngx_addon_dir/src/ngx_http_lua_proxy_ssl_certby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_proxy_ssl_verifyby.h \\\n            $ngx_addon_dir/src/ngx_http_lua_log_ringbuf.h \\\n            $ngx_addon_dir/src/ngx_http_lua_input_filters.h \\\n            $ngx_addon_dir/src/ngx_http_lua_pipe.h \\\n            $ngx_addon_dir/src/ngx_http_lua_worker_thread.h \\\n            \"\n\n# ----------------------------------------\n\nngx_feature=\"export symbols by default (-E)\"\nngx_feature_libs=\"-Wl,-E\"\nngx_feature_name=\nngx_feature_run=no\nngx_feature_incs=\"#include <stdio.h>\"\nngx_feature_path=\nngx_feature_test='printf(\"hello\");'\n\n. auto/feature\n\nif [ $ngx_found = yes ]; then\n    CORE_LIBS=\"-Wl,-E $CORE_LIBS\"\nfi\n\n# ----------------------------------------\n\n# for Cygwin\nngx_feature=\"export symbols by default (--export-all-symbols)\"\nngx_feature_libs=\"-Wl,--export-all-symbols\"\nngx_feature_name=\nngx_feature_run=no\nngx_feature_incs=\"#include <stdio.h>\"\nngx_feature_path=\nngx_feature_test='printf(\"hello\");'\n\n. auto/feature\n\nif [ $ngx_found = yes ]; then\n    CORE_LIBS=\"-Wl,--export-all-symbols $CORE_LIBS\"\nfi\n\n# ----------------------------------------\n\nngx_feature=\"SO_PASSCRED\"\nngx_feature_libs=\nngx_feature_name=\"NGX_HTTP_LUA_HAVE_SO_PASSCRED\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/types.h>\n#include <sys/socket.h>\"\nngx_feature_path=\nngx_feature_test='setsockopt(1, SOL_SOCKET, SO_PASSCRED, NULL, 0);'\n\n. auto/feature\n\n# ----------------------------------------\n\nngx_feature=\"SA_RESTART\"\nngx_feature_libs=\nngx_feature_name=\"NGX_HTTP_LUA_HAVE_SA_RESTART\"\nngx_feature_run=no\nngx_feature_incs=\"#include <signal.h>\"\nngx_feature_path=\nngx_feature_test='struct sigaction act;\n                  act.sa_flags |= SA_RESTART;'\n\n. auto/feature\n\n# ----------------------------------------\n\nngx_feature=\"malloc_trim\"\nngx_feature_libs=\nngx_feature_name=\"NGX_HTTP_LUA_HAVE_MALLOC_TRIM\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <malloc.h>\n#include <stdio.h>\"\nngx_feature_test=\"int rc = malloc_trim((size_t) 0); printf(\\\"%d\\\", rc);\"\nSAVED_CC_TEST_FLAGS=\"$CC_TEST_FLAGS\"\nCC_TEST_FLAGS=\"-Werror -Wall $CC_TEST_FLAGS\"\n\n. auto/feature\n\nCC_TEST_FLAGS=\"$SAVED_CC_TEST_FLAGS\"\n\n# ----------------------------------------\n\nngx_feature=\"pipe2\"\nngx_feature_libs=\nngx_feature_name=\"NGX_HTTP_LUA_HAVE_PIPE2\"\nngx_feature_run=no\nngx_feature_incs=\"#include <fcntl.h>\"\nngx_feature_test=\"int fd[2]; pipe2(fd, O_CLOEXEC|O_NONBLOCK);\"\nSAVED_CC_TEST_FLAGS=\"$CC_TEST_FLAGS\"\nCC_TEST_FLAGS=\"-Werror -Wall $CC_TEST_FLAGS\"\n\n. auto/feature\n\nCC_TEST_FLAGS=\"$SAVED_CC_TEST_FLAGS\"\n\n# ----------------------------------------\n\nngx_feature=\"signalfd\"\nngx_feature_libs=\nngx_feature_name=\"NGX_HTTP_LUA_HAVE_SIGNALFD\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/signalfd.h>\"\nngx_feature_test=\"sigset_t set = { 0 };\n                  signalfd(-1, &set, SFD_NONBLOCK|SFD_CLOEXEC);\"\nSAVED_CC_TEST_FLAGS=\"$CC_TEST_FLAGS\"\nCC_TEST_FLAGS=\"-Werror -Wall $CC_TEST_FLAGS\"\n\n. auto/feature\n\nCC_TEST_FLAGS=\"$SAVED_CC_TEST_FLAGS\"\n\n# ----------------------------------------\n\nngx_feature=\"execvpe\"\nngx_feature_libs=\nngx_feature_name=\"NGX_HTTP_LUA_HAVE_EXECVPE\"\nngx_feature_run=no\nngx_feature_incs=\nngx_feature_test='char* argv[] = {\"/bin/sh\"};execvpe(\"/bin/sh\", argv, NULL);'\nSAVED_CC_TEST_FLAGS=\"$CC_TEST_FLAGS\"\nCC_TEST_FLAGS=\"-Werror -Wall $CC_TEST_FLAGS\"\n\n. auto/feature\n\nCC_TEST_FLAGS=\"$SAVED_CC_TEST_FLAGS\"\n\n# ----------------------------------------\n\nif [ -n \"$ngx_module_link\" ]; then\n    ngx_module_type=HTTP_AUX_FILTER\n    ngx_module_name=$ngx_addon_name\n    ngx_module_deps=\"$HTTP_LUA_DEPS\"\n    ngx_module_srcs=\"$HTTP_LUA_SRCS\"\n\n    . auto/module\nelse\n    HTTP_AUX_FILTER_MODULES=\"$HTTP_AUX_FILTER_MODULES $ngx_addon_name\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $HTTP_LUA_SRCS\"\n    NGX_ADDON_DEPS=\"$NGX_ADDON_DEPS $HTTP_LUA_DEPS\"\n\n    CORE_INCS=\"$CORE_INCS $ngx_module_incs\"\n    CORE_LIBS=\"$CORE_LIBS $ngx_module_libs\"\nfi\n\n# ----------------------------------------\n\nUSE_MD5=YES\nUSE_SHA1=YES\n\nNGX_DTRACE_PROVIDERS=\"$NGX_DTRACE_PROVIDERS $ngx_addon_dir/dtrace/ngx_lua_provider.d\"\nNGX_TAPSET_SRCS=\"$NGX_TAPSET_SRCS $ngx_addon_dir/tapset/ngx_lua.stp\"\n\nCORE_INCS=\"$CORE_INCS $ngx_addon_dir/src/api\"\n\nCFLAGS=\"$CFLAGS -DNDK_SET_VAR\"\n\necho \"/* DO NOT EDIT! This file was automatically generated by config */\" > \"$ngx_addon_dir/src/ngx_http_lua_autoconf.h\"\n"
  },
  {
    "path": "doc/HttpLuaModule.wiki",
    "content": "= Name =\n\nngx_http_lua_module - Embed the power of Lua into Nginx HTTP Servers.\n\nThis module is a core component of [https://openresty.org OpenResty]. If you are using this module,\nthen you are essentially using OpenResty.\n\n''This module is not distributed with the Nginx source.'' See\n[[#Installation|the installation instructions]].\n\nThis is a core component of OpenResty. If you are using this module, then you are essentially using OpenResty :)\n\n= Status =\n\nProduction ready.\n\n= Version =\n\nThis document describes ngx_lua\n[https://github.com/openresty/lua-nginx-module/tags v0.10.25], which was released\non 19 June 2023.\n\n= Videos =\n\n* YouTube video \"[Hello World HTTP Example with OpenResty/Lua](https://youtu.be/eSfYLvVQMxw)\"\n\n    [![Hello World HTTP Example with OpenResty/Lua](https://img.youtube.com/vi/eSfYLvVQMxw/0.jpg)](https://youtu.be/eSfYLvVQMxw)\n\n* YouTube video \"[Write Your Own Lua Modules in OpenResty/Nginx Applications](https://youtu.be/vfYxOMl5LVY)\"\n\n    [![Write Your Own Lua Modules in OpenResty/Nginx Applications](https://img.youtube.com/vi/vfYxOMl5LVY/0.jpg)](https://youtu.be/vfYxOMl5LVY)\n\n* YouTube video \"[OpenResty's resty Command-Line Utility Demo](https://youtu.be/L1c7aw4mSOo)\"\n\n    [![OpenResty's resty Command-Line Utility Demo](https://img.youtube.com/vi/L1c7aw4mSOo/0.jpg)](https://youtu.be/L1c7aw4mSOo)\n\n* YouTube video \"[Measure Execution Time of Lua Code Correctly in OpenResty](https://youtu.be/VkRYW_qLoME)\"\n\n    [![Measure Execution Time of Lua Code Correctly in OpenResty](https://img.youtube.com/vi/VkRYW_qLoME/0.jpg)](https://youtu.be/VkRYW_qLoME)\n\n* YouTube video \"[Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup](https://youtu.be/EP7c0BM2yNo)\"\n\n    [![Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup](https://img.youtube.com/vi/EP7c0BM2yNo/0.jpg)](https://youtu.be/EP7c0BM2yNo)\n\nYou are welcome to subscribe to our [official YouTube channel, OpenResty](https://www.youtube.com/channel/UCXVmwF-UCScv2ftsGoMqxhw).\n\n= Synopsis =\n<geshi lang=\"nginx\">\n    # set search paths for pure Lua external libraries (';;' is the default path):\n    lua_package_path '/foo/bar/?.lua;/blah/?.lua;;';\n\n    # set search paths for Lua external libraries written in C (can also use ';;'):\n    lua_package_cpath '/bar/baz/?.so;/blah/blah/?.so;;';\n\n    server {\n        location /lua_content {\n            # MIME type determined by default_type:\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say('Hello,world!')\n            }\n        }\n\n        location /nginx_var {\n            # MIME type determined by default_type:\n            default_type 'text/plain';\n\n            # try access /nginx_var?a=hello,world\n            content_by_lua_block {\n                ngx.say(ngx.var.arg_a)\n            }\n        }\n\n        location = /request_body {\n            client_max_body_size 50k;\n            client_body_buffer_size 50k;\n\n            content_by_lua_block {\n                ngx.req.read_body()  -- explicitly read the req body\n                local data = ngx.req.get_body_data()\n                if data then\n                    ngx.say(\"body data:\")\n                    ngx.print(data)\n                    return\n                end\n\n                -- body may get buffered in a temp file:\n                local file = ngx.req.get_body_file()\n                if file then\n                    ngx.say(\"body is in file \", file)\n                else\n                    ngx.say(\"no body found\")\n                end\n            }\n        }\n\n        # transparent non-blocking I/O in Lua via subrequests\n        # (well, a better way is to use cosockets)\n        location = /lua {\n            # MIME type determined by default_type:\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                local res = ngx.location.capture(\"/some_other_location\")\n                if res then\n                    ngx.say(\"status: \", res.status)\n                    ngx.say(\"body:\")\n                    ngx.print(res.body)\n                end\n            }\n        }\n\n        location = /foo {\n            rewrite_by_lua_block {\n                res = ngx.location.capture(\"/memc\",\n                    { args = { cmd = \"incr\", key = ngx.var.uri } }\n                )\n            }\n\n            proxy_pass http://blah.blah.com;\n        }\n\n        location = /mixed {\n            rewrite_by_lua_file /path/to/rewrite.lua;\n            access_by_lua_file /path/to/access.lua;\n            content_by_lua_file /path/to/content.lua;\n        }\n\n        # use nginx var in code path\n        # CAUTION: contents in nginx var must be carefully filtered,\n        # otherwise there'll be great security risk!\n        location ~ ^/app/([-_a-zA-Z0-9/]+) {\n            set $path $1;\n            content_by_lua_file /path/to/lua/app/root/$path.lua;\n        }\n\n        location / {\n           client_max_body_size 100k;\n           client_body_buffer_size 100k;\n\n           access_by_lua_block {\n               -- check the client IP address is in our black list\n               if ngx.var.remote_addr == \"132.5.72.3\" then\n                   ngx.exit(ngx.HTTP_FORBIDDEN)\n               end\n\n               -- check if the URI contains bad words\n               if ngx.var.uri and\n                      string.match(ngx.var.request_body, \"evil\")\n               then\n                   return ngx.redirect(\"/terms_of_use.html\")\n               end\n\n               -- tests passed\n           }\n\n           # proxy_pass/fastcgi_pass/etc settings\n        }\n    }\n</geshi>\n\n= Description =\n\nThis module embeds [https://luajit.org/luajit.html LuaJIT 2.0/2.1] into Nginx.\nIt is a core component of [https://openresty.org OpenResty]. If you are using\nthis module, then you are essentially using OpenResty.\n\nSince version <code>v0.10.16</code> of this module, the standard Lua\ninterpreter (also known as \"PUC-Rio Lua\") is not supported anymore. This\ndocument interchangeably uses the terms \"Lua\" and \"LuaJIT\" to refer to the\nLuaJIT interpreter.\n\nBy leveraging Nginx's subrequests, this module allows the integration of the\npowerful Lua threads (known as Lua \"coroutines\") into the Nginx event model.\n\nUnlike [https://httpd.apache.org/docs/trunk/mod/mod_lua.html Apache's mod_lua]\nand [http://redmine.lighttpd.net/wiki/1/Docs:ModMagnet Lighttpd's mod_magnet],\nLua code executed using this module can be ''100% non-blocking'' on network\ntraffic as long as the [[#Nginx API for Lua|Nginx API for Lua]] provided by\nthis module is used to handle requests to upstream services such as MySQL,\nPostgreSQL, Memcached, Redis, or upstream HTTP web services.\n\nAt least the following Lua libraries and Nginx modules can be used with this\nmodule:\n\n* [https://github.com/openresty/lua-resty-memcached lua-resty-memcached]\n* [https://github.com/openresty/lua-resty-mysql lua-resty-mysql]\n* [https://github.com/openresty/lua-resty-redis lua-resty-redis]\n* [https://github.com/openresty/lua-resty-dns lua-resty-dns]\n* [https://github.com/openresty/lua-resty-upload lua-resty-upload]\n* [https://github.com/openresty/lua-resty-websocket lua-resty-websocket]\n* [https://github.com/openresty/lua-resty-lock lua-resty-lock]\n* [https://github.com/cloudflare/lua-resty-logger-socket lua-resty-logger-socket]\n* [https://github.com/openresty/lua-resty-lrucache lua-resty-lrucache]\n* [https://github.com/openresty/lua-resty-string lua-resty-string]\n* [[HttpMemcModule|ngx_memc]]\n* [https://github.com/FRiCKLE/ngx_postgres ngx_postgres]\n* [[HttpRedis2Module|ngx_redis2]]\n* [[HttpRedisModule|ngx_redis]]\n* [[HttpProxyModule|ngx_proxy]]\n* [[HttpFastcgiModule|ngx_fastcgi]]\n\nAlmost any Nginx modules can be used with this ngx_lua module by means of\n[[#ngx.location.capture|ngx.location.capture]] or\n[[#ngx.location.capture_multi|ngx.location.capture_multi]] but it is\nrecommended to use those <code>lua-resty-*</code> libraries instead of creating\nsubrequests to access the Nginx upstream modules because the former is usually\nmuch more flexible and memory-efficient.\n\nThe Lua interpreter (also known as \"Lua State\" or \"LuaJIT VM instance\") is\nshared across all the requests in a single Nginx worker process to minimize\nmemory use. Request contexts are segregated using lightweight Lua coroutines.\n\nLoaded Lua modules persist in the Nginx worker process level resulting in a\nsmall memory footprint in Lua even when under heavy loads.\n\nThis module is plugged into Nginx's \"http\" subsystem so it can only speaks\ndownstream communication protocols in the HTTP family (HTTP 0.9/1.0/1.1/2.0,\nWebSockets, etc...).  If you want to do generic TCP communications with the\ndownstream clients, then you should use the\n[https://github.com/openresty/stream-lua-nginx-module#readme ngx_stream_lua]\nmodule instead, which offers a compatible Lua API.\n\n= Typical Uses =\n\nJust to name a few:\n\n* Mashup'ing and processing outputs of various Nginx upstream outputs (proxy, drizzle, postgres, redis, memcached, and etc) in Lua,\n* doing arbitrarily complex access control and security checks in Lua before requests actually reach the upstream backends,\n* manipulating response headers in an arbitrary way (by Lua)\n* 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,\n* coding up arbitrarily complex web applications in a content handler using synchronous but still non-blocking access to the database backends and other storage,\n* doing very complex URL dispatch in Lua at rewrite phase,\n* using Lua to implement advanced caching mechanism for Nginx's subrequests and arbitrary locations.\n\nThe possibilities are unlimited as the module allows bringing together various\nelements within Nginx as well as exposing the power of the Lua language to the\nuser. The module provides the full flexibility of scripting while offering\nperformance levels comparable with native C language programs both in terms of\nCPU time as well as memory footprint thanks to LuaJIT 2.x.\n\nOther scripting language implementations typically struggle to match this\nperformance level.\n\n= Nginx Compatibility =\n\nThe latest version of this module is compatible with the following versions of Nginx:\n\n* 1.19.x  (last tested: 1.19.3)\n* 1.17.x  (last tested: 1.17.8)\n* 1.15.x  (last tested: 1.15.8)\n* 1.14.x\n* 1.13.x  (last tested: 1.13.6)\n* 1.12.x\n* 1.11.x  (last tested: 1.11.2)\n* 1.10.x\n* 1.9.x (last tested: 1.9.15)\n* 1.8.x\n* 1.7.x (last tested: 1.7.10)\n* 1.6.x\n\nNginx cores older than 1.6.0 (exclusive) are *not* supported.\n\n= Installation =\n\nIt is *highly* recommended to use [https://openresty.org OpenResty releases]\nwhich bundle Nginx, ngx_lua (this module), LuaJIT, as well as other powerful\ncompanion Nginx modules and Lua libraries.\n\nIt is discouraged to build this module with Nginx yourself since it is tricky\nto set up exactly right.\n\nNote that Nginx, LuaJIT, and OpenSSL official releases have various limitations\nand long standing bugs that can cause some of this module's features to be\ndisabled, not work properly, or run slower. Official OpenResty releases are\nrecommended because they bundle [https://github.com/openresty/luajit2\nOpenResty's optimized LuaJIT 2.1 fork] and\n[https://github.com/openresty/openresty/tree/master/patches Nginx/OpenSSL\npatches].\n\nAlternatively, ngx_lua can be manually compiled into Nginx:\n\n# LuaJIT can be downloaded from the [https://github.com/openresty/luajit2/releases latest release of OpenResty's LuaJIT fork]. The official LuaJIT 2.x releases are also supported, although performance will be significantly lower for reasons elaborated above\n# Download the latest version of the ngx_devel_kit (NDK) module [https://github.com/simplresty/ngx_devel_kit/tags HERE]\n# Download the latest version of ngx_lua [https://github.com/openresty/lua-nginx-module/tags HERE]\n# Download the latest supported version of Nginx [https://nginx.org/ HERE] (See [[#Nginx Compatibility|Nginx Compatibility]])\n# Download the latest version of the lua-resty-core [HERE](https://github.com/openresty/lua-resty-core)\n# Download the latest version of the lua-resty-lrucache [HERE](https://github.com/openresty/lua-resty-lrucache)\n\nBuild the source with this module:\n\n<geshi lang=\"bash\">\n    wget 'https://openresty.org/download/nginx-1.19.3.tar.gz'\n    tar -xzvf nginx-1.19.3.tar.gz\n    cd nginx-1.19.3/\n\n    # tell nginx's build system where to find LuaJIT 2.0:\n    export LUAJIT_LIB=/path/to/luajit/lib\n    export LUAJIT_INC=/path/to/luajit/include/luajit-2.0\n\n    # tell nginx's build system where to find LuaJIT 2.1:\n    export LUAJIT_LIB=/path/to/luajit/lib\n    export LUAJIT_INC=/path/to/luajit/include/luajit-2.1\n\n    # Here we assume Nginx is to be installed under /opt/nginx/.\n    ./configure --prefix=/opt/nginx \\\n            --with-ld-opt=\"-Wl,-rpath,/path/to/luajit/lib\" \\\n            --add-module=/path/to/ngx_devel_kit \\\n            --add-module=/path/to/lua-nginx-module\n\n    # Note that you may also want to add `./configure` options which are used in your\n    # current nginx build.\n    # You can get usually those options using command nginx -V\n\n    # you can change the parallelism number 2 below to fit the number of spare CPU cores in your\n    # machine.\n    make -j2\n    make install\n\n    # Note that this version of lug-nginx-module not allow to set `lua_load_resty_core off;` any more.\n    # So, you have to install `lua-resty-core` and `lua-resty-lrucache` manually as below.\n\n    cd lua-resty-core\n    make install PREFIX=/opt/nginx\n    cd lua-resty-lrucache\n    make install PREFIX=/opt/nginx\n\n    # add necessary `lua_package_path` directive to `nginx.conf`, in the http context\n\n    lua_package_path \"/opt/nginx/lib/lua/?.lua;;\";\n</geshi>\n\n== Building as a dynamic module ==\n\nStarting from NGINX 1.9.11, you can also compile this module as a dynamic module, by using the <code>--add-dynamic-module=PATH</code> option instead of <code>--add-module=PATH</code> on the\n<code>./configure</code> command line above. And then you can explicitly load the module in your <code>nginx.conf</code> via the [load_module](https://nginx.org/en/docs/ngx_core_module.html#load_module)\ndirective, for example,\n\n<geshi lang=\"nginx\">\nload_module /path/to/modules/ndk_http_module.so;  # assuming NDK is built as a dynamic module too\nload_module /path/to/modules/ngx_http_lua_module.so;\n</geshi>\n\n== C Macro Configurations ==\n\nWhile building this module either via OpenResty or with the Nginx core, you can define the following C macros via the C compiler options:\n\n* <code>NGX_LUA_USE_ASSERT</code>\n: 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 <code>v0.9.10</code> release.\n* <code>NGX_LUA_ABORT_AT_PANIC</code>\n: 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 <code>v0.9.8</code> release.\n\nTo enable one or more of these macros, just pass extra C compiler options to the <code>./configure</code> script of either Nginx or OpenResty. For instance,\n\n<geshi>\n    ./configure --with-cc-opt=\"-DNGX_LUA_USE_ASSERT -DNGX_LUA_ABORT_AT_PANIC\"\n</geshi>\n\n= Community =\n\n== English Mailing List ==\n\nThe [https://groups.google.com/group/openresty-en openresty-en] mailing list is for English speakers.\n\n== Chinese Mailing List ==\n\nThe [https://groups.google.com/group/openresty openresty] mailing list is for Chinese speakers.\n\n= Code Repository =\n\nThe code repository of this project is hosted on GitHub at\n[https://github.com/openresty/lua-nginx-module openresty/lua-nginx-module].\n\n= Bugs and Patches =\n\nPlease submit bug reports, wishlists, or patches by\n\n# creating a ticket on the [https://github.com/openresty/lua-nginx-module/issues GitHub Issue Tracker],\n# or posting to the [[#Community|OpenResty community]].\n\n= LuaJIT bytecode support =\n\nWatch YouTube video \"[Measure Execution Time of Lua Code Correctly in OpenResty](https://youtu.be/VkRYW_qLoME)\"\n\n[![Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup](https://img.youtube.com/vi/EP7c0BM2yNo/0.jpg)](https://youtu.be/EP7c0BM2yNo)\n\nAs from the <code>v0.5.0rc32</code> release, all <code>*_by_lua_file</code> configure directives (such as [[#content_by_lua_file|content_by_lua_file]]) support loading LuaJIT 2.0/2.1 raw bytecode files directly:\n\n<geshi lang=\"bash\">\n    /path/to/luajit/bin/luajit -b /path/to/input_file.lua /path/to/output_file.ljbc\n</geshi>\n\nThe <code>-bg</code> option can be used to include debug information in the LuaJIT bytecode file:\n\n<geshi lang=\"bash\">\n    /path/to/luajit/bin/luajit -bg /path/to/input_file.lua /path/to/output_file.ljbc\n</geshi>\n\nPlease refer to the official LuaJIT documentation on the <code>-b</code> option for more details:\n\nhttps://luajit.org/running.html#opt_b\n\nNote that the bytecode files generated by LuaJIT 2.1 is ''not'' compatible with\nLuaJIT 2.0, and vice versa. The support for LuaJIT 2.1 bytecode was first added\nin ngx_lua v0.9.3.\n\nAttempts to load standard Lua 5.1 bytecode files into ngx_lua instances linked\nto LuaJIT 2.0/2.1 (or vice versa) will result in an Nginx error message such as\nthe one below:\n\n<geshi lang=\"text\">\n    [error] 13909#0: *1 failed to load Lua inlined code: bad byte-code header in /path/to/test_file.luac\n</geshi>\n\nLoading bytecode files via the Lua primitives like <code>require</code> and\n<code>dofile</code> should always work as expected.\n\n= System Environment Variable Support =\n\nIf you want to access the system environment variable, say, <code>foo</code>, in Lua via the standard Lua API [https://www.lua.org/manual/5.1/manual.html#pdf-os.getenv os.getenv], then you should also list this environment variable name in your <code>nginx.conf</code> file via the [https://nginx.org/en/docs/ngx_core_module.html#env env directive]. For example,\n\n<geshi lang=\"nginx\">\n    env foo;\n</geshi>\n\n= HTTP 1.0 support =\n\nThe HTTP 1.0 protocol does not support chunked output and requires an explicit <code>Content-Length</code> header when the response body is not empty in order to support the HTTP 1.0 keep-alive.\nSo when a HTTP 1.0 request is made and the [[#lua_http10_buffering|lua_http10_buffering]] directive is turned <code>on</code>, ngx_lua will buffer the\noutput of [[#ngx.say|ngx.say]] and [[#ngx.print|ngx.print]] calls and also postpone sending response headers until all the response body output is received.\nAt that time ngx_lua can calculate the total length of the body and construct a proper <code>Content-Length</code> header to return to the HTTP 1.0 client.\nIf the <code>Content-Length</code> response header is set in the running Lua code, however, this buffering will be disabled even if the [[#lua_http10_buffering|lua_http10_buffering]] directive is turned <code>on</code>.\n\nFor large streaming output responses, it is important to disable the [[#lua_http10_buffering|lua_http10_buffering]] directive to minimise memory usage.\n\nNote that common HTTP benchmark tools such as <code>ab</code> and <code>http_load</code> issue HTTP 1.0 requests by default.\nTo force <code>curl</code> to send HTTP 1.0 requests, use the <code>-0</code> option.\n\n= Statically Linking Pure Lua Modules =\n\nWith LuaJIT 2.x, it is possible to statically link the bytecode of pure Lua\nmodules into the Nginx executable.\n\nYou can use the <code>luajit</code> executable to compile <code>.lua</code> Lua\nmodule files to <code>.o</code> object files containing the exported bytecode\ndata, and then link the <code>.o</code> files directly in your Nginx build.\n\nBelow is a trivial example to demonstrate this. Consider that we have the following <code>.lua</code> file named <code>foo.lua</code>:\n\n<geshi lang=\"lua\">\n    -- foo.lua\n    local _M = {}\n\n    function _M.go()\n        print(\"Hello from foo\")\n    end\n\n    return _M\n</geshi>\n\nAnd then we compile this <code>.lua</code> file to <code>foo.o</code> file:\n\n<geshi lang=\"bash\">\n    /path/to/luajit/bin/luajit -bg foo.lua foo.o\n</geshi>\n\nWhat matters here is the name of the <code>.lua</code> file, which determines how you use this module later on the Lua land. The file name <code>foo.o</code> does not matter at all except the <code>.o</code> file extension (which tells <code>luajit</code> what output format is used). If you want to strip the Lua debug information from the resulting bytecode, you can just specify the <code>-b</code> option above instead of <code>-bg</code>.\n\nThen when building Nginx or OpenResty, pass the <code>--with-ld-opt=\"foo.o\"</code> option to the <code>./configure</code> script:\n\n<geshi lang=\"bash\">\n    ./configure --with-ld-opt=\"/path/to/foo.o\" ...\n</geshi>\n\nFinally, you can just do the following in any Lua code run by ngx_lua:\n\n<geshi lang=\"lua\">\n    local foo = require \"foo\"\n    foo.go()\n</geshi>\n\nAnd this piece of code no longer depends on the external <code>foo.lua</code> file any more because it has already been compiled into the <code>nginx</code> executable.\n\nIf you want to use dot in the Lua module name when calling <code>require</code>, as in\n\n<geshi lang=\"lua\">\n    local foo = require \"resty.foo\"\n</geshi>\n\nthen you need to rename the <code>foo.lua</code> file to <code>resty_foo.lua</code> before compiling it down to a <code>.o</code> file with the <code>luajit</code> command-line utility.\n\nIt is important to use exactly the same version of LuaJIT when compiling <code>.lua</code> files to <code>.o</code> files as building nginx + ngx_lua. This is because the LuaJIT bytecode format may be incompatible between different LuaJIT versions. When the bytecode format is incompatible, you will see a Lua runtime error saying that the Lua module is not found.\n\nWhen you have multiple <code>.lua</code> files to compile and link, then just specify their <code>.o</code> files at the same time in the value of the <code>--with-ld-opt</code> option. For instance,\n\n<geshi lang=\"bash\">\n    ./configure --with-ld-opt=\"/path/to/foo.o /path/to/bar.o\" ...\n</geshi>\n\nIf you have too many <code>.o</code> files, then it might not be feasible to name them all in a single command. In this case, you can build a static library (or archive) for your <code>.o</code> files, as in\n\n<geshi lang=\"bash\">\n    ar rcus libmyluafiles.a *.o\n</geshi>\n\nthen you can link the <code>myluafiles</code> archive as a whole to your nginx executable:\n\n<geshi lang=\"bash\">\n    ./configure \\\n        --with-ld-opt=\"-L/path/to/lib -Wl,--whole-archive -lmyluafiles -Wl,--no-whole-archive\"\n</geshi>\n\nwhere <code>/path/to/lib</code> is the path of the directory containing the <code>libmyluafiles.a</code> file. It should be noted that the linker option <code>--whole-archive</code> is required here because otherwise our archive will be skipped because no symbols in our archive are mentioned in the main parts of the nginx executable.\n\n= Data Sharing within an Nginx Worker =\n\nTo globally share data among all the requests handled by the same Nginx worker\nprocess, encapsulate the shared data into a Lua module, use the Lua\n<code>require</code> builtin to import the module, and then manipulate the\nshared data in Lua. This works because required Lua modules are loaded only\nonce and all coroutines will share the same copy of the module (both its code\nand data).\n\nNote that the use of global Lua variables is *strongly discouraged*, as it may\nlead to unexpected race conditions between concurrent requests.\n\nHere is a small example on sharing data within an Nginx worker via a Lua module:\n\n<geshi lang=\"lua\">\n    -- mydata.lua\n    local _M = {}\n\n    local data = {\n        dog = 3,\n        cat = 4,\n        pig = 5,\n    }\n\n    function _M.get_age(name)\n        return data[name]\n    end\n\n    return _M\n</geshi>\n\nand then accessing it from <code>nginx.conf</code>:\n\n<geshi lang=\"nginx\">\n    location /lua {\n        content_by_lua_block {\n            local mydata = require \"mydata\"\n            ngx.say(mydata.get_age(\"dog\"))\n        }\n    }\n</geshi>\n\nThe <code>mydata</code> module in this example will only be loaded and run on the first request to the location <code>/lua</code>,\nand all subsequent requests to the same Nginx worker process will use the reloaded instance of the\nmodule as well as the same copy of the data in it, until a <code>HUP</code> signal is sent to the Nginx master process to force a reload.\nThis data sharing technique is essential for high performance Lua applications based on this module.\n\nNote that this data sharing is on a ''per-worker'' basis and not on a ''per-server'' basis. That is, when there are multiple Nginx worker processes under an Nginx master, data sharing cannot cross the process boundary between these workers.\n\nIt is usually recommended to share read-only data this way. You can also share changeable data among all the concurrent requests of each Nginx worker process as\nlong as there is ''no'' nonblocking I/O operations (including [[#ngx.sleep|ngx.sleep]])\nin the middle of your calculations. As long as you do not give the\ncontrol back to the Nginx event loop and ngx_lua's light thread\nscheduler (even implicitly), there can never be any race conditions in\nbetween. For this reason, always be very careful when you want to share changeable data on the\nworker level. Buggy optimizations can easily lead to hard-to-debug\nrace conditions under load.\n\nIf server-wide data sharing is required, then use one or more of the following approaches:\n\n# Use the [[#ngx.shared.DICT|ngx.shared.DICT]] API provided by this module.\n# Use only a single Nginx worker and a single server (this is however not recommended when there is a multi core CPU or multiple CPUs in a single machine).\n# Use data storage mechanisms such as <code>memcached</code>, <code>redis</code>, <code>MySQL</code> or <code>PostgreSQL</code>. [https://openresty.org The OpenResty official releases] come with a set of companion Nginx modules and Lua libraries that provide interfaces with these data storage mechanisms.\n\n= Known Issues =\n\n== TCP socket connect operation issues ==\n\nThe [[#tcpsock:connect|tcpsock:connect]] method may indicate <code>success</code> despite connection failures such as with <code>Connection Refused</code> errors.\n\nHowever, later attempts to manipulate the cosocket object will fail and return the actual error status message generated by the failed connect operation.\n\nThis issue is due to limitations in the Nginx event model and only appears to affect Mac OS X.\n\n== Lua Coroutine Yielding/Resuming ==\n\n* Because Lua's <code>dofile</code> and <code>require</code> builtins are currently implemented as C functions in LuaJIT 2.0/2.1, if the Lua file being loaded by <code>dofile</code> or <code>require</code> invokes [[#ngx.location.capture|ngx.location.capture*]], [[#ngx.exec|ngx.exec]], [[#ngx.exit|ngx.exit]], or other API functions requiring yielding in the *top-level* scope of the Lua file, then the Lua error \"attempt to yield across C-call boundary\" will be raised. To avoid this, put these calls requiring yielding into your own Lua functions in the Lua file instead of the top-level scope of the file.\n\n== Lua Variable Scope ==\n\nCare must be taken when importing modules, and this form should be used:\n\n<geshi lang=\"lua\">\n    local xxx = require('xxx')\n</geshi>\n\ninstead of the old deprecated form:\n\n<geshi lang=\"lua\">\n    require('xxx')\n</geshi>\n\nHere is the reason: by design, the global environment has exactly the same lifetime as the Nginx request handler associated with it. Each request handler has its own set of Lua global variables and that is the idea of request isolation. The Lua module is actually loaded by the first Nginx request handler and is cached by the <code>require()</code> built-in in the <code>package.loaded</code> table for later reference, and the <code>module()</code> builtin used by some Lua modules has the side effect of setting a global variable to the loaded module table. But this global variable will be cleared at the end of the request handler,  and every subsequent request handler all has its own (clean) global environment. So one will get Lua exception for accessing the <code>nil</code> value.\n\nThe use of Lua global variables is a generally inadvisable in the ngx_lua context as:\n\n# the misuse of Lua globals has detrimental side effects on concurrent requests when such variables should instead be local in scope,\n# Lua global variables require Lua table look-ups in the global environment which is computationally expensive, and\n# some Lua global variable references may include typing errors which make such difficult to debug.\n\nIt is therefore *highly* recommended to always declare such within an appropriate local scope instead.\n\n<geshi lang=\"lua\">\n    -- Avoid\n    foo = 123\n    -- Recommended\n    local foo = 123\n\n    -- Avoid\n    function foo() return 123 end\n    -- Recommended\n    local function foo() return 123 end\n</geshi>\n\nTo find all instances of Lua global variables in your Lua code, run the [https://github.com/openresty/nginx-devel-utils/blob/master/lua-releng lua-releng tool] across all <code>.lua</code> source files:\n<geshi lang=\"text\">\n$ lua-releng\nChecking use of Lua global variables in file lib/foo/bar.lua ...\n        1       [1489]  SETGLOBAL       7 -1    ; contains\n        55      [1506]  GETGLOBAL       7 -3    ; setvar\n        3       [1545]  GETGLOBAL       3 -4    ; varexpand\n</geshi>\nThe output says that the line 1489 of file <code>lib/foo/bar.lua</code> writes to a global variable named <code>contains</code>, the line 1506 reads from the global variable <code>setvar</code>, and line 1545 reads the global <code>varexpand</code>.\n\nThis tool will guarantee that local variables in the Lua module functions are all declared with the <code>local</code> keyword, otherwise a runtime exception will be thrown. It prevents undesirable race conditions while accessing such variables. See [[#Data_Sharing_within_an_Nginx_Worker|Data Sharing within an Nginx Worker]] for the reasons behind this.\n\n== Locations Configured by Subrequest Directives of Other Modules ==\n\nThe [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]] directives cannot capture locations that include the [[HttpAdditionModule#add_before_body|add_before_body]], [[HttpAdditionModule#add_after_body|add_after_body]], [https://nginx.org/en/docs/http/ngx_http_auth_request_module.html#auth_request auth_request], [[HttpEchoModule#echo_location|echo_location]], [[HttpEchoModule#echo_location_async|echo_location_async]], [[HttpEchoModule#echo_subrequest|echo_subrequest]], or [[HttpEchoModule#echo_subrequest_async|echo_subrequest_async]] directives.\n\n<geshi lang=\"nginx\">\n    location /foo {\n        content_by_lua_block {\n            res = ngx.location.capture(\"/bar\")\n        }\n    }\n    location /bar {\n        echo_location /blah;\n    }\n    location /blah {\n        echo \"Success!\";\n    }\n</geshi>\n\n<geshi lang=\"nginx\">\n    $ curl -i http://example.com/foo\n</geshi>\n\nwill not work as expected.\n\n== Cosockets Not Available Everywhere ==\n\nDue to internal limitations in the Nginx core, the cosocket API is disabled in the following contexts: [[#set_by_lua|set_by_lua*]], [[#log_by_lua|log_by_lua*]], [[#header_filter_by_lua|header_filter_by_lua*]], and [[#body_filter_by_lua|body_filter_by_lua]].\n\nThe cosockets are currently also disabled in the [[#init_by_lua|init_by_lua*]] and [[#init_worker_by_lua|init_worker_by_lua*]] directive contexts but we may add support for these contexts in the future because there is no limitation in the Nginx core (or the limitation might be worked around).\n\nThere exists a workaround, however, when the original context does *not* need to wait for the cosocket results. That is, creating a zero-delay timer via the [[#ngx.timer.at|ngx.timer.at]] API and do the cosocket results in the timer handler, which runs asynchronously as to the original context creating the timer.\n\n== Special Escaping Sequences ==\n\n'''NOTE''' Following the <code>v0.9.17</code> release, this pitfall can be avoided by using the <code>*_by_lua_block {}</code> configuration directives.\n\nPCRE sequences such as <code>\\d</code>, <code>\\s</code>, or <code>\\w</code>, require special attention because in string literals, the backslash character, <code>\\</code>, is stripped out by both the Lua language parser and by the Nginx config file parser before processing if not within a <code>*_by_lua_block {}</code> directive. So the following snippet will not work as expected:\n\n<geshi lang=\"nginx\">\n    # nginx.conf\n    ? location /test {\n    ?     content_by_lua '\n    ?         local regex = \"\\d+\"  -- THIS IS WRONG OUTSIDE OF A *_by_lua_block DIRECTIVE\n    ?         local m = ngx.re.match(\"hello, 1234\", regex)\n    ?         if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n    ?     ';\n    ? }\n    # evaluates to \"not matched!\"\n</geshi>\n\nTo avoid this, ''double'' escape the backslash:\n\n<geshi lang=\"nginx\">\n    # nginx.conf\n    location /test {\n        content_by_lua '\n            local regex = \"\\\\\\\\d+\"\n            local m = ngx.re.match(\"hello, 1234\", regex)\n            if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n        ';\n    }\n    # evaluates to \"1234\"\n</geshi>\n\nHere, <code>\\\\\\\\d+</code> is stripped down to <code>\\\\d+</code> by the Nginx config file parser and this is further stripped down to <code>\\d+</code> by the Lua language parser before running.\n\nAlternatively, the regex pattern can be presented as a long-bracketed Lua string literal by encasing it in \"long brackets\", <code>&#91;[...]]</code>, in which case backslashes have to only be escaped once for the Nginx config file parser.\n\n<geshi lang=\"nginx\">\n    # nginx.conf\n    location /test {\n        content_by_lua '\n            local regex = [[\\\\d+]]\n            local m = ngx.re.match(\"hello, 1234\", regex)\n            if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n        ';\n    }\n    # evaluates to \"1234\"\n</geshi>\n\nHere, <code>&#91;[\\\\d+]]</code> is stripped down to <code>&#91;[\\d+]]</code> by the Nginx config file parser and this is processed correctly.\n\nNote that a longer from of the long bracket, <code>[=[...]=]</code>, may be required if the regex pattern contains <code>&#91;...]</code> sequences.\nThe <code>[=[...]=]</code> form may be used as the default form if desired.\n\n<geshi lang=\"nginx\">\n    # nginx.conf\n    location /test {\n        content_by_lua '\n            local regex = [=[[0-9]+]=]\n            local m = ngx.re.match(\"hello, 1234\", regex)\n            if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n        ';\n    }\n    # evaluates to \"1234\"\n</geshi>\n\nAn alternative approach to escaping PCRE sequences is to ensure that Lua code is placed in external script files and executed using the various <code>*_by_lua_file</code> directives.\nWith this approach, the backslashes are only stripped by the Lua language parser and therefore only need to be escaped once each.\n\n<geshi lang=\"lua\">\n    -- test.lua\n    local regex = \"\\\\d+\"\n    local m = ngx.re.match(\"hello, 1234\", regex)\n    if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n    -- evaluates to \"1234\"\n</geshi>\n\nWithin external script files, PCRE sequences presented as long-bracketed Lua string literals do not require modification.\n\n<geshi lang=\"lua\">\n    -- test.lua\n    local regex = [[\\d+]]\n    local m = ngx.re.match(\"hello, 1234\", regex)\n    if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n    -- evaluates to \"1234\"\n</geshi>\n\nAs noted earlier, PCRE sequences presented within <code>*_by_lua_block {}</code> directives (available following the <code>v0.9.17</code> release) do not require modification.\n\n<geshi lang=\"nginx\">\n    # nginx.conf\n    location /test {\n        content_by_lua_block {\n            local regex = [[\\d+]]\n            local m = ngx.re.match(\"hello, 1234\", regex)\n            if m then ngx.say(m[0]) else ngx.say(\"not matched!\") end\n        }\n    }\n    # evaluates to \"1234\"\n</geshi>\n\n'''NOTE''' You are recommended to use `by_lua_file` when the Lua code is very long.\n\n== Mixing with SSI Not Supported ==\n\nMixing 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.\n\n== SPDY Mode Not Fully Supported ==\n\nCertain Lua APIs provided by ngx_lua do not work in Nginx's SPDY mode yet: [[#ngx.location.capture|ngx.location.capture]], [[#ngx.location.capture_multi|ngx.location.capture_multi]], and [[#ngx.req.socket|ngx.req.socket]].\n\n== Missing data on short circuited requests ==\n\nNginx may terminate a request early with (at least):\n\n* 400 (Bad Request)\n* 405 (Not Allowed)\n* 408 (Request Timeout)\n* 413 (Request Entity Too Large)\n* 414 (Request URI Too Large)\n* 494 (Request Headers Too Large)\n* 499 (Client Closed Request)\n* 500 (Internal Server Error)\n* 501 (Not Implemented)\n\nThis means that phases that normally run are skipped, such as the rewrite or\naccess phase. This also means that later phases that are run regardless, e.g.\n[[#log_by_lua|log_by_lua]], will not have access to information that is normally set in those\nphases.\n\n= TODO =\n\n* cosocket: implement LuaSocket's unconnected UDP API.\n* cosocket: add support in the context of [[#init_by_lua|init_by_lua*]].\n* cosocket: implement the <code>bind()</code> method for stream-typed cosockets.\n* cosocket: review and merge aviramc's [https://github.com/openresty/lua-nginx-module/pull/290 patch] for adding the <code>bsdrecv</code> method.\n* cosocket: add configure options for different strategies of handling the cosocket connection exceeding in the pools.\n* review and apply vadim-pavlov's patch for [[#ngx.location.capture|ngx.location.capture]]'s <code>extra_headers</code> option\n* use <code>ngx_hash_t</code> to optimize the built-in header look-up process for [[#ngx.req.set_header|ngx.req.set_header]], and etc.\n* add <code>ignore_resp_headers</code>, <code>ignore_resp_body</code>, and <code>ignore_resp</code> 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.\n* add automatic Lua code time slicing support by yielding and resuming the Lua VM actively via Lua's debug hooks.\n* add <code>stat</code> mode similar to [https://httpd.apache.org/docs/trunk/mod/mod_lua.html mod_lua].\n\n= Changes =\n\nThe changes made in every release of this module are listed in the change logs of the OpenResty bundle:\n\nhttps://openresty.org/#Changes\n\n= Test Suite =\n\nThe following dependencies are required to run the test suite:\n\n* Nginx version >= 1.4.2\n\n* Perl modules:\n** Test::Nginx: https://github.com/openresty/test-nginx\n\n* Nginx modules:\n** [https://github.com/simplresty/ngx_devel_kit ngx_devel_kit]\n** [https://github.com/openresty/set-misc-nginx-module ngx_set_misc]\n** [http://mdounin.ru/files/ngx_http_auth_request_module-0.2.tar.gz ngx_auth_request] (this is not needed if you're using Nginx 1.5.4+.\n** [https://github.com/openresty/echo-nginx-module ngx_echo]\n** [https://github.com/openresty/memc-nginx-module ngx_memc]\n** [https://github.com/openresty/srcache-nginx-module ngx_srcache]\n** ngx_lua (i.e., this module)\n** [https://github.com/openresty/lua-upstream-nginx-module ngx_lua_upstream]\n** [https://github.com/openresty/headers-more-nginx-module ngx_headers_more]\n** [https://github.com/openresty/drizzle-nginx-module ngx_drizzle]\n** [https://github.com/openresty/rds-json-nginx-module ngx_rds_json]\n** [https://github.com/FRiCKLE/ngx_coolkit ngx_coolkit]\n** [https://github.com/openresty/redis2-nginx-module ngx_redis2]\n\nThe order in which these modules are added during configuration is important because the position of any filter module in the\nfiltering chain determines the final output, for example. The correct adding order is shown above.\n\n* 3rd-party Lua libraries:\n** [http://www.kyne.com.au/~mark/software/lua-cjson.php lua-cjson]\n\n* Applications:\n** mysql: create database 'ngx_test', grant all privileges to user 'ngx_test', password is 'ngx_test'\n** memcached: listening on the default port, 11211.\n** redis: listening on the default port, 6379.\n\nSee also the [https://github.com/openresty/lua-nginx-module/blob/master/util/build.sh developer build script] for more details on setting up the testing environment.\n\nTo run the whole test suite in the default testing mode:\n<geshi lang=\"text\">\n    cd /path/to/lua-nginx-module\n    export PATH=/path/to/your/nginx/sbin:$PATH\n    prove -I/path/to/test-nginx/lib -r t\n</geshi>\n\nTo run specific test files:\n<geshi lang=\"text\">\n    cd /path/to/lua-nginx-module\n    export PATH=/path/to/your/nginx/sbin:$PATH\n    prove -I/path/to/test-nginx/lib t/002-content.t t/003-errors.t\n</geshi>\n\nTo run a specific test block in a particular test file, add the line <code>--- ONLY</code> to the test block you want to run, and then use the <code>prove</code> utility to run that <code>.t</code> file.\n\nThere are also various testing modes based on mockeagain, valgrind, and etc. Refer to the [https://search.cpan.org/perldoc?Test::Nginx Test::Nginx documentation] for more details for various advanced testing modes. See also the test reports for the Nginx test cluster running on Amazon EC2: https://qa.openresty.org.\n\n= Copyright and License =\n\nThis module is licensed under the BSD license.\n\nCopyright (C) 2009-2017, by Xiaozhe Wang (chaoslawful) <chaoslawful@gmail.com>.\n\nCopyright (C) 2009-2019, by Yichun \"agentzh\" Zhang (章亦春) <agentzh@gmail.com>, OpenResty Inc.\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n= See Also =\n\nBlog posts:\n\n* [Introduction to Lua-Land CPU Flame Graphs](https://blog.openresty.com/en/lua-cpu-flame-graph/?src=gh_ngxlua)\n* [How OpenResty and Nginx Allocate and Manage Memory](https://blog.openresty.com/en//how-or-alloc-mem?src=gh_ngxlua)\n* [How OpenResty and Nginx Shared Memory Zones Consume RAM](https://blog.openresty.com/en/how-nginx-shm-consume-ram/?src=gh_ngxlua)\n* [Memory Fragmentation in OpenResty and Nginx's Shared Memory Zones](https://blog.openresty.com/en/nginx-shm-frag/?src=gh_ngxlua)\n\nOther related modules and libraries:\n\n* [https://github.com/openresty/stream-lua-nginx-module#readme ngx_stream_lua_module] for an official port of this module for the Nginx \"stream\" subsystem (doing generic downstream TCP communications).\n* [https://github.com/openresty/lua-resty-memcached lua-resty-memcached] library based on ngx_lua cosocket.\n* [https://github.com/openresty/lua-resty-redis lua-resty-redis] library based on ngx_lua cosocket.\n* [https://github.com/openresty/lua-resty-mysql lua-resty-mysql] library based on ngx_lua cosocket.\n* [https://github.com/openresty/lua-resty-upload lua-resty-upload] library based on ngx_lua cosocket.\n* [https://github.com/openresty/lua-resty-dns lua-resty-dns] library based on ngx_lua cosocket.\n* [https://github.com/openresty/lua-resty-websocket lua-resty-websocket] library for both WebSocket server and client, based on ngx_lua cosocket.\n* [https://github.com/openresty/lua-resty-string lua-resty-string] library based on [https://luajit.org/ext_ffi.html LuaJIT FFI].\n* [https://github.com/openresty/lua-resty-lock lua-resty-lock] library for a nonblocking simple lock API.\n* [https://github.com/cloudflare/lua-resty-cookie lua-resty-cookie] library for HTTP cookie manipulation.\n* [https://openresty.org/#RoutingMySQLQueriesBasedOnURIArgs Routing requests to different MySQL queries based on URI arguments]\n* [https://openresty.org/#DynamicRoutingBasedOnRedis Dynamic Routing Based on Redis and Lua]\n* [https://openresty.org/#UsingLuaRocks Using LuaRocks with ngx_lua]\n* [https://github.com/openresty/lua-nginx-module/wiki/Introduction Introduction to ngx_lua]\n* [https://github.com/simplresty/ngx_devel_kit ngx_devel_kit]\n* [[HttpEchoModule]]\n* [[HttpDrizzleModule]]\n* [https://github.com/FRiCKLE/ngx_postgres postgres-nginx-module]\n* [[HttpMemcModule]]\n* [https://openresty.org The OpenResty bundle]\n* [https://github.com/openresty/nginx-systemtap-toolkit Nginx Systemtap Toolkit]\n\n= Directives =\n\n<!-- inline-toc -->\n\nThe basic building blocks of scripting Nginx with Lua are directives. Directives are used to specify when the user Lua code is run and\nhow the result will be used. Below is a diagram showing the order in which directives are executed.\n\n![Lua Nginx Modules Directives](https://cloud.githubusercontent.com/assets/2137369/15272097/77d1c09e-1a37-11e6-97ef-d9767035fc3e.png)\n\n== lua_load_resty_core ==\n\n'''syntax:''' ''lua_load_resty_core on|off''\n\n'''default:''' ''lua_load_resty_core on''\n\n'''context:''' ''http''\n\nThis directive is deprecated since the <code>v0.10.16</code> release of this\nmodule. The <code>resty.core</code> module from\n[https://github.com/openresty/lua-resty-core lua-resty-core] is now mandatorily\nloaded during the Lua VM initialization. Specifying this directive will have no\neffect.\n\nThis directive was first introduced in the <code>v0.10.15</code> release and\nused to optionally load the <code>resty.core</code> module.\n\n== lua_capture_error_log ==\n\n'''syntax:''' ''lua_capture_error_log size''\n\n'''default:''' ''none''\n\n'''context:''' ''http''\n\nEnables a buffer of the specified <code>size</code> for capturing all the Nginx error log message data (not just those produced\nby this module or the Nginx http subsystem, but everything) without touching files or disks.\n\nYou can use units like `k` and `m` in the <code>size</code> value, as in\n\n<geshi lang=\"nginx\">\n    lua_capture_error_log 100k;\n</geshi>\n\nAs a rule of thumb, a 4KB buffer can usually hold about 20 typical error log messages. So do the maths!\n\nThis buffer never grows. If it is full, new error log messages will replace the oldest ones in the buffer.\n\nThe size of the buffer must be bigger than the maximum length of a single error log message (which is 4K in OpenResty and 2K in stock NGINX).\n\nYou can read the messages in the buffer on the Lua land via the\n[https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/errlog.md#get_logs get_logs()]\nfunction of the\n[https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/errlog.md#readme ngx.errlog]\nmodule of the [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/errlog.md#readme lua-resty-core]\nlibrary. This Lua API function will return the captured error log messages and\nalso remove these already read from the global capturing buffer, making room\nfor any new error log data. For this reason, the user should not configure this\nbuffer to be too big if the user read the buffered error log data fast enough.\n\nNote that the log level specified in the standard [https://nginx.org/r/error_log error_log] directive\n''does'' have effect on this capturing facility. It only captures log\nmessages of a level no lower than the specified log level in the [https://nginx.org/r/error_log error_log] directive.\nThe user can still choose to set an even higher filtering log level on the fly via the Lua API function\n[https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/errlog.md#set_filter_level errlog.set_filter_level].\nSo it is more flexible than the static [https://nginx.org/r/error_log error_log] directive.\n\nIt is worth noting that there is no way to capture the debugging logs\nwithout building OpenResty or Nginx with the <code>./configure</code>\noption <code>--with-debug</code>. And enabling debugging logs is\nstrongly discouraged in production builds due to high overhead.\n\nThis directive was first introduced in the <code>v0.10.9</code> release.\n\n== lua_use_default_type ==\n\n'''syntax:''' ''lua_use_default_type on | off''\n\n'''default:''' ''lua_use_default_type on''\n\n'''context:''' ''http, server, location, location if''\n\nSpecifies whether to use the MIME type specified by the [https://nginx.org/en/docs/http/ngx_http_core_module.html#default_type default_type] directive for the default value of the <code>Content-Type</code> response header. Deactivate this directive if a default <code>Content-Type</code> response header for Lua request handlers is not desired.\n\nThis directive is turned on by default.\n\nThis directive was first introduced in the <code>v0.9.1</code> release.\n\n== lua_malloc_trim ==\n\n'''syntax:''' ''lua_malloc_trim <request-count>''\n\n'''default:''' ''lua_malloc_trim 1000''\n\n'''context:''' ''http''\n\nAsks the underlying <code>libc</code> runtime library to release its cached free memory back to the operating system every\n<code>N</code> requests processed by the Nginx core. By default, <code>N</code> is 1000. You can configure the request count\nby using your own numbers. Smaller numbers mean more frequent releases, which may introduce higher CPU time consumption and\nsmaller memory footprint while larger numbers usually lead to less CPU time overhead and relatively larger memory footprint.\nJust tune the number for your own use cases.\n\nConfiguring the argument to <code>0</code> essentially turns off the periodical memory trimming altogether.\n\n<geshi lang=\"nginx\">\n    lua_malloc_trim 0;  # turn off trimming completely\n</geshi>\n\nThe current implementation uses an Nginx log phase handler to do the request counting. So the appearance of the\n[https://nginx.org/en/docs/http/ngx_http_core_module.html#log_subrequest log_subrequest on] directives in <code>nginx.conf</code>\nmay make the counting faster when subrequests are involved. By default, only \"main requests\" count.\n\nNote that this directive does *not* affect the memory allocated by LuaJIT's own allocator based on the <code>mmap</code>\nsystem call.\n\nThis directive was first introduced in the <code>v0.10.7</code> release.\n\n== lua_code_cache ==\n'''syntax:''' ''lua_code_cache on | off''\n\n'''default:''' ''lua_code_cache on''\n\n'''context:''' ''http, server, location, location if''\n\nEnables or disables the Lua code cache for Lua code in <code>*_by_lua_file</code> directives (like [[#set_by_lua_file|set_by_lua_file]] and\n[[#content_by_lua_file|content_by_lua_file]]) and Lua modules.\n\nWhen turning off, every request served by ngx_lua will run in a separate Lua VM instance, starting from the <code>0.9.3</code> release. So the Lua files referenced in [[#set_by_lua_file|set_by_lua_file]],\n[[#content_by_lua_file|content_by_lua_file]], [[#access_by_lua_file|access_by_lua_file]],\nand etc will not be cached\nand all Lua modules used will be loaded from scratch. With this in place, developers can adopt an edit-and-refresh approach.\n\nPlease note however, that Lua code written inlined within nginx.conf\nsuch as those specified by [[#set_by_lua|set_by_lua]], [[#content_by_lua|content_by_lua]],\n[[#access_by_lua|access_by_lua]], and [[#rewrite_by_lua|rewrite_by_lua]] will not be updated when you edit the inlined Lua code in your <code>nginx.conf</code> file because only the Nginx config file parser can correctly parse the <code>nginx.conf</code>\nfile and the only way is to reload the config file\nby sending a <code>HUP</code> signal or just to restart Nginx.\n\nEven when the code cache is enabled, Lua files which are loaded by <code>dofile</code> or <code>loadfile</code>\nin *_by_lua_file cannot be cached (unless you cache the results yourself). Usually you can either use the [[#init_by_lua|init_by_lua]]\nor [[#init-by_lua_file|init_by_lua_file]] directives to load all such files or just make these Lua files true Lua modules\nand load them via <code>require</code>.\n\nThe ngx_lua module does not support the <code>stat</code> mode available with the\nApache <code>mod_lua</code> module (yet).\n\nDisabling the Lua code cache is strongly\ndiscouraged for production use and should only be used during\ndevelopment as it has a significant negative impact on overall performance. For example, the performance of a \"hello world\" Lua example can drop by an order of magnitude after disabling the Lua code cache.\n\n== lua_thread_cache_max_entries ==\n\n'''syntax:''' ''lua_thread_cache_max_entries <num>''\n\n'''default:''' ''lua_thread_cache_max_entries 1024''\n\n'''context:''' ''http''\n\nSpecifies the maximum number of entries allowed in the worker process level lua thread object cache.\n\nThis cache recycles the lua thread GC objects among all our \"light threads\".\n\nA zero value of `<num>` disables the cache.\n\nNote that this feature requires OpenResty's LuaJIT with the new C API `lua_resetthread`.\n\nThis feature was first introduced in verson `v0.10.9`.\n\n== lua_regex_cache_max_entries ==\n\n'''syntax:''' ''lua_regex_cache_max_entries <num>''\n\n'''default:''' ''lua_regex_cache_max_entries 1024''\n\n'''context:''' ''http''\n\nSpecifies the maximum number of entries allowed in the worker process level compiled regex cache.\n\nThe regular expressions used in [[#ngx.re.match|ngx.re.match]], [[#ngx.re.gmatch|ngx.re.gmatch]], [[#ngx.re.sub|ngx.re.sub]], and [[#ngx.re.gsub|ngx.re.gsub]] will be cached within this cache if the regex option <code>o</code> (i.e., compile-once flag) is specified.\n\nThe default number of entries allowed is 1024 and when this limit is reached, new regular expressions will not be cached (as if the <code>o</code> option was not specified) and there will be one, and only one, warning in the <code>error.log</code> file:\n\n<geshi lang=\"text\">\n    2011/08/27 23:18:26 [warn] 31997#0: *1 lua exceeding regex cache max entries (1024), ...\n</geshi>\n\nIf you are using the <code>ngx.re.*</code> implementation of [lua-resty-core](https://github.com/openresty/lua-resty-core) by loading the <code>resty.core.regex</code> module (or just the <code>resty.core</code> module), then an LRU cache is used for the regex cache being used here.\n\nDo not activate the <code>o</code> option for regular expressions (and/or <code>replace</code> string arguments for [[#ngx.re.sub|ngx.re.sub]] and [[#ngx.re.gsub|ngx.re.gsub]]) that are generated ''on the fly'' and give rise to infinite variations to avoid hitting the specified limit.\n\n== lua_regex_match_limit ==\n\n'''syntax:''' ''lua_regex_match_limit <num>''\n\n'''default:''' ''lua_regex_match_limit 0''\n\n'''context:''' ''http''\n\nSpecifies the \"match limit\" used by the PCRE library when executing the [[#ngx.re.match|ngx.re API]]. To quote the PCRE manpage, \"the limit ... has the effect of limiting the amount of backtracking that can take place.\"\n\nWhen the limit is hit, the error string \"pcre_exec() failed: -8\" will be returned by the [[#ngx.re.match|ngx.re API]] functions on the Lua land.\n\nWhen setting the limit to 0, the default \"match limit\" when compiling the PCRE library is used. And this is the default value of this directive.\n\nThis directive was first introduced in the <code>v0.8.5</code> release.\n\n== lua_package_path ==\n\n'''syntax:''' ''lua_package_path <lua-style-path-str>''\n\n'''default:''' ''The content of LUA_PATH environment variable or Lua's compiled-in defaults.''\n\n'''context:''' ''http''\n\nSets the Lua module search path used by scripts specified by [[#set_by_lua|set_by_lua]],\n[[#content_by_lua|content_by_lua]] and others. The path string is in standard Lua path form, and <code>;;</code>\ncan be used to stand for the original search paths.\n\nAs from the <code>v0.5.0rc29</code> release, the special notation <code>$prefix</code> or <code>${prefix}</code> can be used in the search path string to indicate the path of the <code>server prefix</code> usually determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\n== lua_package_cpath ==\n\n'''syntax:''' ''lua_package_cpath <lua-style-cpath-str>''\n\n'''default:''' ''The content of LUA_CPATH environment variable or Lua's compiled-in defaults.''\n\n'''context:''' ''http''\n\nSets the Lua C-module search path used by scripts specified by [[#set_by_lua|set_by_lua]],\n[[#content_by_lua|content_by_lua]] and others. The cpath string is in standard Lua cpath form, and <code>;;</code>\ncan be used to stand for the original cpath.\n\nAs from the <code>v0.5.0rc29</code> release, the special notation <code>$prefix</code> or <code>${prefix}</code> can be used in the search path string to indicate the path of the <code>server prefix</code> usually determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\n== init_by_lua ==\n\n'''syntax:''' ''init_by_lua <lua-script-str>''\n\n'''context:''' ''http''\n\n'''phase:''' ''loading-config''\n\n'''NOTE''' Use of this directive is ''discouraged'' following the <code>v0.9.17</code> release. Use the [[#init_by_lua_block|init_by_lua_block]] directive instead.\n\nSimilar to the [[#init_by_lua_block|init_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires\nspecial character escaping).\n\nFor instance,\n\n<geshi lang=\"nginx\">\n    init_by_lua '\n        print(\"I need no extra escaping here, for example: \\r\\nblah\")\n    '\n</geshi>\n\nThis directive was first introduced in the <code>v0.5.5</code> release.\n\n== init_by_lua_block ==\n\n'''syntax:''' ''init_by_lua_block { lua-script }''\n\n'''context:''' ''http''\n\n'''phase:''' ''loading-config''\n\n\nWhen Nginx receives the <code>HUP</code> signal and starts reloading the config file, the Lua VM will also be re-created and <code>init_by_lua_block</code> 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 <code>init_by_lua_block</code> handler will run upon every request because in this special mode a standalone Lua VM is always created for each request.\n\nUsually 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:\n\n<geshi lang=\"nginx\">\n    # this runs before forking out nginx worker processes:\n    init_by_lua_block { require \"cjson\" }\n\n    server {\n        location = /api {\n            content_by_lua_block {\n                -- the following require() will just  return\n                -- the already loaded module from package.loaded:\n                ngx.say(require \"cjson\".encode{dog = 5, cat = 6})\n            }\n        }\n    }\n</geshi>\n\nYou can also initialize the [[#lua_shared_dict|lua_shared_dict]] shm storage at this phase. Here is an example for this:\n\n<geshi lang=\"nginx\">\n    lua_shared_dict dogs 1m;\n\n    init_by_lua_block {\n        local dogs = ngx.shared.dogs\n        dogs:set(\"Tom\", 56)\n    }\n\n    server {\n        location = /api {\n            content_by_lua_block {\n                local dogs = ngx.shared.dogs\n                ngx.say(dogs:get(\"Tom\"))\n            }\n        }\n    }\n</geshi>\n\nBut note that, the [[#lua_shared_dict|lua_shared_dict]]'s shm storage will not be cleared through a config reload (via the <code>HUP</code> signal, for example). So if you do ''not'' want to re-initialize the shm storage in your <code>init_by_lua_block</code> code in this case, then you just need to set a custom flag in the shm storage and always check the flag in your <code>init_by_lua_block</code> code.\n\nBecause 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.\n\nDo *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 <code>init_by_lua_block</code> or other contexts ([https://www.lua.org/manual/5.1/manual.html#pdf-require require()] does cache the loaded Lua modules in the global <code>package.loaded</code> table in the Lua registry so your modules will only loaded once for the whole Lua VM instance).\n\nOnly a small set of the [[#Nginx API for Lua|Nginx API for Lua]] is supported in this context:\n\n* Logging APIs: [[#ngx.log|ngx.log]] and [[#print|print]],\n* Shared Dictionary API: [[#ngx.shared.DICT|ngx.shared.DICT]].\n\nMore Nginx APIs for Lua may be supported in this context upon future user requests.\n\nBasically you can safely use Lua libraries that do blocking I/O in this very context because blocking the master process during server start-up is completely okay. Even the Nginx core does blocking I/O (at least on resolving upstream's host names) at the configure-loading phase.\n\nYou 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 <code>root</code> account.\n\nThis directive was first introduced in the <code>v0.9.17</code> release.\n\nSee also the following blog posts for more details on OpenResty and Nginx's shared memory zones:\n\n* [How OpenResty and Nginx Shared Memory Zones Consume RAM](https://blog.openresty.com/en/how-nginx-shm-consume-ram/?src=gh_ngxlua)\n* [Memory Fragmentation in OpenResty and Nginx's Shared Memory Zones](https://blog.openresty.com/en/nginx-shm-frag/?src=gh_ngxlua)\n\n== init_by_lua_file ==\n\n'''syntax:''' ''init_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''http''\n\n'''phase:''' ''loading-config''\n\nEquivalent to [[#init_by_lua_block|init_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code or [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nThis directive was first introduced in the <code>v0.5.5</code> release.\n\n== init_worker_by_lua ==\n\n'''syntax:''' ''init_worker_by_lua <lua-script-str>''\n\n'''context:''' ''http''\n\n'''phase:''' ''starting-worker''\n\n'''NOTE''' Use of this directive is ''discouraged'' following the <code>v0.9.17</code> release. Use the [[#init_worker_by_lua_block|init_worker_by_lua_block]] directive instead.\n\nSimilar 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\nspecial character escaping).\n\nFor instance,\n\n<geshi lang=\"nginx\">\n    init_worker_by_lua '\n        print(\"I need no extra escaping here, for example: \\r\\nblah\")\n    ';\n</geshi>\n\nThis directive was first introduced in the <code>v0.9.5</code> release.\n\nThis hook no longer runs in the cache manager and cache loader processes since the <code>v0.10.12</code> release.\n\n== init_worker_by_lua_block ==\n\n'''syntax:''' ''init_worker_by_lua_block { lua-script }''\n\n'''context:''' ''http''\n\n'''phase:''' ''starting-worker''\n\nRuns 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*]].\n\nThis 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,\n\n<geshi lang=\"nginx\">\n    init_worker_by_lua_block {\n        local delay = 3  -- in seconds\n        local new_timer = ngx.timer.at\n        local log = ngx.log\n        local ERR = ngx.ERR\n        local check\n\n        check = function(premature)\n            if not premature then\n                -- do the health check or other routine work\n                local ok, err = new_timer(delay, check)\n                if not ok then\n                    log(ERR, \"failed to create timer: \", err)\n                    return\n                end\n            end\n\n            -- do something in timer\n        end\n\n        local hdl, err = new_timer(delay, check)\n        if not hdl then\n            log(ERR, \"failed to create timer: \", err)\n            return\n        end\n\n        -- other job in init_worker_by_lua\n    }\n</geshi>\n\nThis directive was first introduced in the <code>v0.9.17</code> release.\n\nThis hook no longer runs in the cache manager and cache loader processes since the <code>v0.10.12</code> release.\n\n== init_worker_by_lua_file ==\n\n'''syntax:''' ''init_worker_by_lua_file <lua-file-path>''\n\n'''context:''' ''http''\n\n'''phase:''' ''starting-worker''\n\nSimilar 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.\n\nThis directive was first introduced in the <code>v0.9.5</code> release.\n\nThis hook no longer runs in the cache manager and cache loader processes since the <code>v0.10.12</code> release.\n\n== exit_worker_by_lua_block ==\n\n'''syntax:''' ''exit_worker_by_lua_block { lua-script }''\n\n'''context:''' ''http''\n\n'''phase:''' ''exiting-worker''\n\nRuns 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.\n\nThis 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.\n\nFor example,\n\n<geshi lang=\"nginx\">\n    exit_worker_by_lua_block {\n        print(\"log from exit_worker_by_lua_block\")\n    }\n</geshi>\n\nIt's not allowed to create a timer (even a 0-delay timer) here since it runs after all timers have been processed.\n\nThis directive was first introduced in the <code>v0.10.18</code> release.\n\n== exit_worker_by_lua_file ==\n\n'''syntax:''' ''exit_worker_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''http''\n\n'''phase:''' ''exiting-worker''\n\nSimilar to [[#exit_worker_by_lua_block|exit_worker_by_lua_block]], but accepts the file path to a Lua source file or Lua bytecode file.\n\nThis directive was first introduced in the <code>v0.10.18</code> release.\n\n== set_by_lua ==\n\n'''syntax:''' ''set_by_lua $res <lua-script-str> [$arg1 $arg2 ...]''\n\n'''context:''' ''server, server if, location, location if''\n\n'''phase:''' ''rewrite''\n\n'''NOTE''' Use of this directive is ''discouraged'' following the <code>v0.9.17</code> release. Use the [[#set_by_lua_block|set_by_lua_block]] directive instead.\n\nSimilar to the [[#set_by_lua_block|set_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires\nspecial character escaping), and\n# this directive support extra arguments after the Lua script.\n\nFor example,\n\n<geshi lang=\"nginx\">\n    set_by_lua $res ' return 32 + math.cos(32) ';\n    # $res now has the value \"32.834223360507\" or alike.\n</geshi>\n\nAs from the <code>v0.5.0rc29</code> release, Nginx variable interpolation is disabled in the <code><lua-script-str></code> argument of this directive and therefore, the dollar sign character (<code>$</code>) can be used directly.\n\nThis directive requires the [https://github.com/simplresty/ngx_devel_kit ngx_devel_kit] module.\n\n== set_by_lua_block ==\n\n'''syntax:''' ''set_by_lua_block $res { lua-script }''\n\n'''context:''' ''server, server if, location, location if''\n\n'''phase:''' ''rewrite''\n\nExecutes code specified inside a pair of curly braces (<code>{}</code>), and returns string output to <code>$res</code>.\nThe code inside a pair of curly braces (<code>{}</code>) can make [[#Nginx API for Lua|API calls]] and can retrieve input arguments from the <code>ngx.arg</code> table (index starts from <code>1</code> and increases sequentially).\n\nThis 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.\n\nThis 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.\n\nAt least the following API functions are currently disabled within the context of <code>set_by_lua_block</code>:\n\n* Output API functions (e.g., [[#ngx.say|ngx.say]] and [[#ngx.send_headers|ngx.send_headers]])\n* Control API functions (e.g., [[#ngx.exit|ngx.exit]])\n* Subrequest API functions (e.g., [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]])\n* Cosocket API functions (e.g., [[#ngx.socket.tcp|ngx.socket.tcp]] and [[#ngx.req.socket|ngx.req.socket]]).\n* Sleeping API function [[#ngx.sleep|ngx.sleep]].\n\nIn addition, note that this directive can only write out a value to a single Nginx variable at\na time. However, a workaround is possible using the [[#ngx.var.VARIABLE|ngx.var.VARIABLE]] interface.\n\n<geshi lang=\"nginx\">\n    location /foo {\n        set $diff ''; # we have to predefine the $diff variable here\n\n        set_by_lua_block $sum {\n            local a = 32\n            local b = 56\n\n            ngx.var.diff = a - b  -- write to $diff directly\n            return a + b          -- return the $sum value normally\n        }\n\n        echo \"sum = $sum, diff = $diff\";\n    }\n</geshi>\n\nThis directive can be freely mixed with all directives of the [[HttpRewriteModule]], [[HttpSetMiscModule]], and [[HttpArrayVarModule]] modules. All of these directives will run in the same order as they appear in the config file.\n\n<geshi lang=\"nginx\">\n    set $foo 32;\n    set_by_lua_block $bar { return tonumber(ngx.var.foo) + 1 }\n    set $baz \"bar: $bar\";  # $baz == \"bar: 33\"\n</geshi>\n\nNo special escaping is required in the Lua code block.\n\nThis directive requires the [https://github.com/simplresty/ngx_devel_kit ngx_devel_kit] module.\n\nThis directive was first introduced in the <code>v0.9.17</code> release.\n\n== set_by_lua_file ==\n\n'''syntax:''' ''set_by_lua_file $res <path-to-lua-script-file> [$arg1 $arg2 ...]''\n\n'''context:''' ''server, server if, location, location if''\n\n'''phase:''' ''rewrite''\n\nEquivalent to [[#set_by_lua_block|set_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or, as from the <code>v0.5.0rc32</code> release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nNginx variable interpolation is supported in the <code><path-to-lua-script-file></code> argument string of this directive. But special care must be taken for injection attacks.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nWhen the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached\nand the Nginx config must be reloaded each time the Lua source file is modified.\nThe Lua code cache can be temporarily disabled during development by\nswitching [[#lua_code_cache|lua_code_cache]] <code>off</code> in <code>nginx.conf</code> to avoid reloading Nginx.\n\nThis directive requires the [https://github.com/simplresty/ngx_devel_kit ngx_devel_kit] module.\n\n== precontent_by_lua_block ==\n\n'''syntax:''' ''precontent_by_lua_block { lua-script }''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''pre-content tail''\n\nActs as a precontent phase handler and executes Lua code string specified in <code>{ <lua-script }</code> for every request.\nThe 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).\n\nNote that this handler always runs *after* the standard [[HttpMirrorModule]] and [[HttpTryFilesModule]]. For example:\n\n<geshi lang=\"nginx\">\n    location /images/ {\n        try_files $uri /images/default.gif;\n        precontent_by_lua_block {\n            ngx.log(ngx.NOTICE, \"file found\")\n        }\n    }\n\n    location = /images/default.gif {\n        expires 30s;\n        precontent_by_lua_block {\n            ngx.log(ngx.NOTICE, \"file not found, use default.gif instead\")\n        }\n    }\n</geshi>\n\nThat is, if a request for /images/foo.jpg comes in and the file does not exist, the request will be internally redirected to /images/default.gif before [[#precontent_by_lua_block|precontent_by_lua_block]], and then the [[#precontent_by_lua_block|precontent_by_lua_block]] in new location will run and log \"file not found, use default.gif instead\".\n\nYou can use [[#precontent_by_lua_block|precontent_by_lua_block]] to perform some preparatory functions after the access phase handler but before the proxy or other content handler. Especially some functions that cannot be performed in [[#balancer_by_lua_block|balancer_by_lua_block]].\n\nYou can use the [[#precontent_by_lua_no_postpone|precontent_by_lua_no_postpone]] directive to control when to run this handler inside the \"precontent\" request-processing phase of Nginx.\n\n== precontent_by_lua_file ==\n\n'''syntax:''' ''precontent_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''precontent tail''\n\nEquivalent to [[#precontent_by_lua_block|precontent_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or, as from the <code>v0.5.0rc32</code> release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nNginx variables can be used in the <code><path-to-lua-script-file></code> string to provide flexibility. This however carries some risks and is not ordinarily recommended.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nWhen the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached\nand the Nginx config must be reloaded each time the Lua source file is modified.\nThe Lua code cache can be temporarily disabled during development by switching [[#lua_code_cache|lua_code_cache]] <code>off</code> in <code>nginx.conf</code> to avoid repeatedly reloading Nginx.\n\nNginx variables are supported in the file path for dynamic dispatch just as in [[#content_by_lua_file|content_by_lua_file]].\n\n== content_by_lua ==\n\n'''syntax:''' ''content_by_lua <lua-script-str>''\n\n'''context:''' ''location, location if''\n\n'''phase:''' ''content''\n\n'''NOTE''' Use of this directive is ''discouraged'' following the <code>v0.9.17</code> release. Use the [[#content_by_lua_block|content_by_lua_block]] directive instead.\n\nSimilar to the [[#content_by_lua_block|content_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires\nspecial character escaping).\n\nFor instance,\n\n<geshi lang=\"nginx\">\n    content_by_lua '\n        ngx.say(\"I need no extra escaping here, for example: \\r\\nblah\")\n    ';\n</geshi>\n\n== content_by_lua_block ==\n\n'''syntax:''' ''content_by_lua_block { lua-script }''\n\n'''context:''' ''location, location if''\n\n'''phase:''' ''content''\n\nFor instance,\n\n<geshi lang=\"nginx\">\n    content_by_lua_block {\n        ngx.say(\"I need no extra escaping here, for example: \\r\\nblah\")\n    }\n</geshi>\n\nActs as a \"content handler\" and executes Lua code string specified in <code>{ lua-script }</code> for every request.\nThe 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).\n\nDo 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.\n\nThis directive was first introduced in the <code>v0.9.17</code> release.\n\n== content_by_lua_file ==\n\n'''syntax:''' ''content_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''location, location if''\n\n'''phase:''' ''content''\n\nEquivalent to [[#content_by_lua_block|content_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or, as from the <code>v0.5.0rc32</code> release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nNginx variables can be used in the <code><path-to-lua-script-file></code> string to provide flexibility. This however carries some risks and is not ordinarily recommended.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nWhen the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached\nand the Nginx config must be reloaded each time the Lua source file is modified.\nThe Lua code cache can be temporarily disabled during development by\nswitching [[#lua_code_cache|lua_code_cache]] <code>off</code> in <code>nginx.conf</code> to avoid reloading Nginx.\n\nNginx variables are supported in the file path for dynamic dispatch, for example:\n\n<geshi lang=\"nginx\">\n    # CAUTION: contents in nginx var must be carefully filtered,\n    # otherwise there'll be great security risk!\n    location ~ ^/app/([-_a-zA-Z0-9/]+) {\n        set $path $1;\n        content_by_lua_file /path/to/lua/app/root/$path.lua;\n    }\n</geshi>\n\nBut be very careful about malicious user inputs and always carefully validate or filter out the user-supplied path components.\n\n== server_rewrite_by_lua_block ==\n\n'''syntax:''' ''server_rewrite_by_lua_block { lua-script }''\n\n'''context:''' ''http, server''\n\n'''phase:''' ''server rewrite''\n\nActs as a server rewrite phase handler and executes Lua code string specified in <code>{ lua-script }</code> for every request.\nThe 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).\n\n<geshi lang=\"nginx\">\nserver {\n    ...\n\n    server_rewrite_by_lua_block {\n        ngx.ctx.a = \"server_rewrite_by_lua_block in http\"\n    }\n\n    location /lua {\n        content_by_lua_block {\n            ngx.say(ngx.ctx.a)\n            ngx.log(ngx.INFO, ngx.ctx.a)\n       \t}\n    }\n}\n</geshi>\n\nJust as any other rewrite phase handlers, [[#server_rewrite_by_lua_block|server_rewrite_by_lua_block]] also runs in subrequests.\n\n<geshi lang=\"nginx\">\nserver {\n    server_rewrite_by_lua_block {\n        ngx.log(ngx.INFO, \"is_subrequest:\", ngx.is_subrequest)\n    }\n\n    location /lua {\n        content_by_lua_block {\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n        }\n    }\n\n    location /sub {\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n}\n</geshi>\n\nNote that when calling <code>ngx.exit(ngx.OK)</code> 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 (<code>ngx.HTTP_OK</code>) and status < 300 (<code>ngx.HTTP_SPECIAL_RESPONSE</code>) for successful quits and <code>ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)</code> (or its friends) for failures.\n\n\n<geshi lang=\"nginx\">\n    server_rewrite_by_lua_block {\n        ngx.exit(503)\n    }\n\n    location /bar {\n        ...\n        # never exec\n    }\n</geshi>\n\n\n== server_rewrite_by_lua_file ==\n\n'''syntax:''' ''server_rewrite_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''http, server''\n\n'''phase:''' ''server rewrite''\n\nEquivalent to [[#server_rewrite_by_lua_block|server_rewrite_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or, as from the <code>v0.10.22</code> release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nNginx variables can be used in the <code><path-to-lua-script-file></code> string to provide flexibility. This however carries some risks and is not ordinarily recommended.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nWhen 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]] <code>off</code> in <code>nginx.conf</code> to avoid reloading Nginx.\n\n== rewrite_by_lua ==\n\n'''syntax:''' ''rewrite_by_lua <lua-script-str>''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''rewrite tail''\n\n'''NOTE''' Use of this directive is ''discouraged'' following the <code>v0.9.17</code> release. Use the [[#rewrite_by_lua_block|rewrite_by_lua_block]] directive instead.\n\nSimilar to the [[#rewrite_by_lua_block|rewrite_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires\nspecial character escaping).\n\nFor instance,\n\n<geshi lang=\"nginx\">\n    rewrite_by_lua '\n        do_something(\"hello, world!\\nhiya\\n\")\n    ';\n</geshi>\n\n== rewrite_by_lua_block ==\n\n'''syntax:''' ''rewrite_by_lua_block { lua-script }''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''rewrite tail''\n\nActs as a rewrite phase handler and executes Lua code string specified in <code>{ lua-script }</code> for every request.\nThe 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).\n\nNote that this handler always runs ''after'' the standard [[HttpRewriteModule]]. So the following will work as expected:\n\n<geshi lang=\"nginx\">\n    location /foo {\n        set $a 12; # create and initialize $a\n        set $b \"\"; # create and initialize $b\n        rewrite_by_lua_block {\n            ngx.var.b = tonumber(ngx.var.a) + 1\n        }\n        echo \"res = $b\";\n    }\n</geshi>\n\nbecause <code>set $a 12</code> and <code>set $b \"\"</code> run ''before'' [[#rewrite_by_lua_block|rewrite_by_lua_block]].\n\nOn the other hand, the following will not work as expected:\n\n<geshi lang=\"nginx\">\n    ?  location /foo {\n    ?      set $a 12; # create and initialize $a\n    ?      set $b ''; # create and initialize $b\n    ?      rewrite_by_lua_block {\n    ?          ngx.var.b = tonumber(ngx.var.a) + 1\n    ?      }\n    ?      if ($b = '13') {\n    ?         rewrite ^ /bar redirect;\n    ?         break;\n    ?      }\n    ?\n    ?      echo \"res = $b\";\n    ?  }\n</geshi>\n\nbecause <code>if</code> 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.\n\nThe right way of doing this is as follows:\n\n<geshi lang=\"nginx\">\n    location /foo {\n        set $a 12; # create and initialize $a\n        set $b ''; # create and initialize $b\n        rewrite_by_lua_block {\n            ngx.var.b = tonumber(ngx.var.a) + 1\n            if tonumber(ngx.var.b) == 13 then\n                return ngx.redirect(\"/bar\")\n            end\n        }\n\n        echo \"res = $b\";\n    }\n</geshi>\n\nNote 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,\n\n<geshi lang=\"nginx\">\n    location / {\n        eval $res {\n            proxy_pass http://foo.com/check-spam;\n        }\n\n        if ($res = 'spam') {\n            rewrite ^ /terms-of-use.html redirect;\n        }\n\n        fastcgi_pass ...;\n    }\n</geshi>\n\ncan be implemented in ngx_lua as:\n\n<geshi lang=\"nginx\">\n    location = /check-spam {\n        internal;\n        proxy_pass http://foo.com/check-spam;\n    }\n\n    location / {\n        rewrite_by_lua_block {\n            local res = ngx.location.capture(\"/check-spam\")\n            if res.body == \"spam\" then\n                return ngx.redirect(\"/terms-of-use.html\")\n            end\n        }\n\n        fastcgi_pass ...;\n    }\n</geshi>\n\nJust as any other rewrite phase handlers, [[#rewrite_by_lua_block|rewrite_by_lua_block]] also runs in subrequests.\n\nNote that when calling <code>ngx.exit(ngx.OK)</code> 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 (<code>ngx.HTTP_OK</code>) and status < 300 (<code>ngx.HTTP_SPECIAL_RESPONSE</code>) for successful quits and <code>ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)</code> (or its friends) for failures.\n\nIf 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,\n\n<geshi lang=\"nginx\">\n    location /foo {\n        rewrite ^ /bar;\n        rewrite_by_lua_block {\n            ngx.exit(503)\n        }\n    }\n    location /bar {\n        ...\n    }\n</geshi>\n\nHere the Lua code <code>ngx.exit(503)</code> will never run. This will be the case if <code>rewrite ^ /bar last</code> is used as this will similarly initiate an internal redirection. If the <code>break</code> modifier is used instead, there will be no internal redirection and the <code>rewrite_by_lua_block</code> code will be executed.\n\nThe <code>rewrite_by_lua_block</code> code will always run at the end of the <code>rewrite</code> request-processing phase unless [[#rewrite_by_lua_no_postpone|rewrite_by_lua_no_postpone]] is turned on.\n\nThis directive was first introduced in the <code>v0.9.17</code> release.\n\n== rewrite_by_lua_file ==\n\n'''syntax:''' ''rewrite_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''rewrite tail''\n\nEquivalent to [[#rewrite_by_lua_block|rewrite_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or, as from the <code>v0.5.0rc32</code> release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nNginx variables can be used in the <code><path-to-lua-script-file></code> string to provide flexibility. This however carries some risks and is not ordinarily recommended.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nWhen 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]] <code>off</code> in <code>nginx.conf</code> to avoid reloading Nginx.\n\nThe <code>rewrite_by_lua_file</code> code will always run at the end of the <code>rewrite</code> request-processing phase unless [[#rewrite_by_lua_no_postpone|rewrite_by_lua_no_postpone]] is turned on.\n\nNginx variables are supported in the file path for dynamic dispatch just as in [[#content_by_lua_file|content_by_lua_file]].\n\n== access_by_lua ==\n\n'''syntax:''' ''access_by_lua <lua-script-str>''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''access tail''\n\n'''NOTE''' Use of this directive is ''discouraged'' following the <code>v0.9.17</code> release. Use the [[#access_by_lua_block|access_by_lua_block]] directive instead.\n\nSimilar to the [[#access_by_lua_block|access_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires\nspecial character escaping).\n\nFor instance,\n\n<geshi lang=\"nginx\">\n    access_by_lua '\n        do_something(\"hello, world!\\nhiya\\n\")\n    ';\n</geshi>\n\n== access_by_lua_block ==\n\n'''syntax:''' ''access_by_lua_block { lua-script }''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''access tail''\n\nActs as an access phase handler and executes Lua code string specified in <code>{ <lua-script }</code> for every request.\nThe 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).\n\nNote that this handler always runs ''after'' the standard [[HttpAccessModule]]. So the following will work as expected:\n\n<geshi lang=\"nginx\">\n    location / {\n        deny    192.168.1.1;\n        allow   192.168.1.0/24;\n        allow   10.1.1.0/16;\n        deny    all;\n\n        access_by_lua_block {\n            local res = ngx.location.capture(\"/mysql\", { ... })\n            ...\n        }\n\n        # proxy_pass/fastcgi_pass/...\n    }\n</geshi>\n\nThat 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]].\n\nNote 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]]:\n\n<geshi lang=\"nginx\">\n    location / {\n        auth_request /auth;\n\n        # proxy_pass/fastcgi_pass/postgres_pass/...\n    }\n</geshi>\n\ncan be implemented in ngx_lua as:\n\n<geshi lang=\"nginx\">\n    location / {\n        access_by_lua_block {\n            local res = ngx.location.capture(\"/auth\")\n\n            if res.status == ngx.HTTP_OK then\n                return\n            end\n\n            if res.status == ngx.HTTP_FORBIDDEN then\n                ngx.exit(res.status)\n            end\n\n            ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\n        }\n\n        # proxy_pass/fastcgi_pass/postgres_pass/...\n    }\n</geshi>\n\nAs with other access phase handlers, [[#access_by_lua_block|access_by_lua_block]] will ''not'' run in subrequests.\n\nNote that when calling <code>ngx.exit(ngx.OK)</code> 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 (<code>ngx.HTTP_OK</code>) and status < 300 (<code>ngx.HTTP_SPECIAL_RESPONSE</code>) for successful quits and <code>ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)</code> (or its friends) for failures.\n\nStarting from the <code>v0.9.20</code> release, you can use the [[#access_by_lua_no_postpone|access_by_lua_no_postpone]]\ndirective to control when to run this handler inside the \"access\" request-processing phase\nof Nginx.\n\nThis directive was first introduced in the <code>v0.9.17</code> release.\n\n== access_by_lua_file ==\n\n'''syntax:''' ''access_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''access tail''\n\nEquivalent to [[#access_by_lua_block|access_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or, as from the <code>v0.5.0rc32</code> release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nNginx variables can be used in the <code><path-to-lua-script-file></code> string to provide flexibility. This however carries some risks and is not ordinarily recommended.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nWhen the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached\nand the Nginx config must be reloaded each time the Lua source file is modified.\nThe Lua code cache can be temporarily disabled during development by switching [[#lua_code_cache|lua_code_cache]] <code>off</code> in <code>nginx.conf</code> to avoid repeatedly reloading Nginx.\n\nNginx variables are supported in the file path for dynamic dispatch just as in [[#content_by_lua_file|content_by_lua_file]].\n\n== header_filter_by_lua ==\n\n'''syntax:''' ''header_filter_by_lua <lua-script-str>''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''output-header-filter''\n\n'''NOTE''' Use of this directive is ''discouraged'' following the <code>v0.9.17</code> release. Use the [[#header_filter_by_lua_block|header_filter_by_lua_block]] directive instead.\n\nSimilar 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\nspecial character escaping).\n\nFor instance,\n\n<geshi lang=\"nginx\">\n    header_filter_by_lua '\n        ngx.header[\"content-length\"] = nil\n    ';\n</geshi>\n\nThis directive was first introduced in the <code>v0.2.1rc20</code> release.\n\n== header_filter_by_lua_block ==\n\n'''syntax:''' ''header_filter_by_lua_block { lua-script }''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''output-header-filter''\n\nUses Lua code specified in <code>{ lua-script }</code> to define an output header filter.\n\nNote that the following API functions are currently disabled within this context:\n\n* Output API functions (e.g., [[#ngx.say|ngx.say]] and [[#ngx.send_headers|ngx.send_headers]])\n* Control API functions (e.g., [[#ngx.redirect|ngx.redirect]] and [[#ngx.exec|ngx.exec]])\n* Subrequest API functions (e.g., [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]])\n* Cosocket API functions (e.g., [[#ngx.socket.tcp|ngx.socket.tcp]] and [[#ngx.req.socket|ngx.req.socket]]).\n\nHere is an example of overriding a response header (or adding one if absent) in our Lua header filter:\n\n<geshi lang=\"nginx\">\n    location / {\n        proxy_pass http://mybackend;\n        header_filter_by_lua_block {\n            ngx.header.Foo = \"blah\"\n        }\n    }\n</geshi>\n\nThis directive was first introduced in the <code>v0.9.17</code> release.\n\n== header_filter_by_lua_file ==\n\n'''syntax:''' ''header_filter_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''output-header-filter''\n\nEquivalent to [[#header_filter_by_lua_block|header_filter_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or as from the <code>v0.5.0rc32</code> release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nThis directive was first introduced in the <code>v0.2.1rc20</code> release.\n\n== body_filter_by_lua ==\n\n'''syntax:''' ''body_filter_by_lua <lua-script-str>''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''output-body-filter''\n\n'''NOTE''' Use of this directive is ''discouraged'' following the <code>v0.9.17</code> release. Use the [[#body_filter_by_lua_block|body_filter_by_lua_block]] directive instead.\n\nSimilar 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\nspecial character escaping).\n\nFor instance,\n\n<geshi lang=\"nginx\">\n    body_filter_by_lua '\n        local data, eof = ngx.arg[1], ngx.arg[2]\n    ';\n</geshi>\n\nThis directive was first introduced in the <code>v0.5.0rc32</code> release.\n\n== body_filter_by_lua_block ==\n\n'''syntax:''' ''body_filter_by_lua_block { lua-script-str }''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''output-body-filter''\n\nUses Lua code specified in <code>{ lua-script }</code> to define an output body filter.\n\nThe 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).\n\nBehind the scene, the \"eof\" flag is just the <code>last_buf</code> (for main requests) or <code>last_in_chain</code> (for subrequests) flag of the Nginx chain link buffers. (Before the <code>v0.7.14</code> release, the \"eof\" flag does not work at all in subrequests.)\n\nThe output data stream can be aborted immediately by running the following Lua statement:\n\n<geshi lang=\"lua\">\n    return ngx.ERROR\n</geshi>\n\nThis will truncate the response body and usually result in incomplete and also invalid responses.\n\nThe Lua code can pass its own modified version of the input data chunk to the downstream Nginx output body filters by overriding [[#ngx.arg|ngx.arg]][1] with a Lua string or a Lua table of strings. For example, to transform all the lowercase letters in the response body, we can just write:\n\n<geshi lang=\"nginx\">\n    location / {\n        proxy_pass http://mybackend;\n        body_filter_by_lua_block {\n            ngx.arg[1] = string.upper(ngx.arg[1])\n        }\n    }\n</geshi>\n\nWhen setting <code>nil</code> or an empty Lua string value to <code>ngx.arg[1]</code>, no data chunk will be passed to the downstream Nginx output filters at all.\n\nLikewise, new \"eof\" flag can also be specified by setting a boolean value to [[#ngx.arg|ngx.arg]][2]. For example,\n\n<geshi lang=\"nginx\">\n    location /t {\n        echo hello world;\n        echo hiya globe;\n\n        body_filter_by_lua_block {\n            local chunk = ngx.arg[1]\n            if string.match(chunk, \"hello\") then\n                ngx.arg[2] = true  -- new eof\n                return\n            end\n\n            -- just throw away any remaining chunk data\n            ngx.arg[1] = nil\n        }\n    }\n</geshi>\n\nThen <code>GET /t</code> will just return the output\n\n<geshi lang=\"text\">\n    hello world\n</geshi>\n\nThat is, when the body filter sees a chunk containing the word \"hello\", then it will set the \"eof\" flag to true immediately, resulting in truncated but still valid responses.\n\nWhen the Lua code may change the length of the response body, then it is required to always clear out the <code>Content-Length</code> response header (if any) in a header filter to enforce streaming output, as in\n\n<geshi lang=\"nginx\">\n    location /foo {\n        # fastcgi_pass/proxy_pass/...\n\n        header_filter_by_lua_block {\n            ngx.header.content_length = nil\n        }\n        body_filter_by_lua_block {\n            ngx.arg[1] = string.len(ngx.arg[1]) .. \"\\n\"\n        }\n    }\n</geshi>\n\nNote that the following API functions are currently disabled within this context due to the limitations in Nginx output filter's current implementation:\n\n* Output API functions (e.g., [[#ngx.say|ngx.say]] and [[#ngx.send_headers|ngx.send_headers]])\n* Control API functions (e.g., [[#ngx.exit|ngx.exit]] and [[#ngx.exec|ngx.exec]])\n* Subrequest API functions (e.g., [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]])\n* Cosocket API functions (e.g., [[#ngx.socket.tcp|ngx.socket.tcp]] and [[#ngx.req.socket|ngx.req.socket]]).\n\nNginx 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.\n\nThis directive was first introduced in the <code>v0.9.17</code> release.\n\n== body_filter_by_lua_file ==\n\n'''syntax:''' ''body_filter_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''output-body-filter''\n\nEquivalent to [[#body_filter_by_lua_block|body_filter_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or, as from the <code>v0.5.0rc32</code> release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nThis directive was first introduced in the <code>v0.5.0rc32</code> release.\n\n== log_by_lua ==\n\n'''syntax:''' ''log_by_lua <lua-script-str>''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''log''\n\n'''NOTE''' Use of this directive is ''discouraged'' following the <code>v0.9.17</code> release. Use the [[#log_by_lua_block|log_by_lua_block]] directive instead.\n\nSimilar to the [[#log_by_lua_block|log_by_lua_block]] directive, but accepts the Lua source directly in an Nginx string literal (which requires\nspecial character escaping).\n\nFor instance,\n\n<geshi lang=\"nginx\">\n    log_by_lua '\n        print(\"I need no extra escaping here, for example: \\r\\nblah\")\n    ';\n</geshi>\n\nThis directive was first introduced in the <code>v0.5.0rc31</code> release.\n\n== log_by_lua_block ==\n\n'''syntax:''' ''log_by_lua_block { lua-script }''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''log''\n\nRuns the Lua source code inlined as the <code>{ lua-script }</code> at the <code>log</code> request processing phase. This does not replace the current access logs, but runs before.\n\nNote that the following API functions are currently disabled within this context:\n\n* Output API functions (e.g., [[#ngx.say|ngx.say]] and [[#ngx.send_headers|ngx.send_headers]])\n* Control API functions (e.g., [[#ngx.exit|ngx.exit]])\n* Subrequest API functions (e.g., [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]])\n* Cosocket API functions (e.g., [[#ngx.socket.tcp|ngx.socket.tcp]] and [[#ngx.req.socket|ngx.req.socket]]).\n\nHere is an example of gathering average data for [[HttpUpstreamModule#$upstream_response_time|$upstream_response_time]]:\n\n<geshi lang=\"nginx\">\n    lua_shared_dict log_dict 5M;\n\n    server {\n        location / {\n            proxy_pass http://mybackend;\n\n            log_by_lua_block {\n                local log_dict = ngx.shared.log_dict\n                local upstream_time = tonumber(ngx.var.upstream_response_time)\n\n                local sum = log_dict:get(\"upstream_time-sum\") or 0\n                sum = sum + upstream_time\n                log_dict:set(\"upstream_time-sum\", sum)\n\n                local newval, err = log_dict:incr(\"upstream_time-nb\", 1)\n                if not newval and err == \"not found\" then\n                    log_dict:add(\"upstream_time-nb\", 0)\n                    log_dict:incr(\"upstream_time-nb\", 1)\n                end\n            }\n        }\n\n        location = /status {\n            content_by_lua_block {\n                local log_dict = ngx.shared.log_dict\n                local sum = log_dict:get(\"upstream_time-sum\")\n                local nb = log_dict:get(\"upstream_time-nb\")\n\n                if nb and sum then\n                    ngx.say(\"average upstream response time: \", sum / nb,\n                            \" (\", nb, \" reqs)\")\n                else\n                    ngx.say(\"no data yet\")\n                end\n            }\n        }\n    }\n</geshi>\n\nThis directive was first introduced in the <code>v0.9.17</code> release.\n\n== log_by_lua_file ==\n\n'''syntax:''' ''log_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''log''\n\nEquivalent to [[#log_by_lua_block|log_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or, as from the <code>v0.5.0rc32</code> release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nThis directive was first introduced in the <code>v0.5.0rc31</code> release.\n\n== balancer_by_lua_block ==\n\n'''syntax:''' ''balancer_by_lua_block { lua-script }''\n\n'''context:''' ''upstream''\n\n'''phase:''' ''content''\n\nThis directive runs Lua code as an upstream balancer for any upstream entities defined\nby the <code>upstream {}</code> configuration block.\n\nFor instance,\n\n<geshi lang=\"nginx\">\n    upstream foo {\n        server 127.0.0.1;\n        balancer_by_lua_block {\n            -- use Lua to do something interesting here\n            -- as a dynamic balancer\n        }\n    }\n\n    server {\n        location / {\n            proxy_pass http://foo;\n        }\n    }\n</geshi>\n\nThe resulting Lua load balancer can work with any existing Nginx upstream modules\nlike [https://nginx.org/en/docs/http/ngx_http_proxy_module.html ngx_proxy] and\n[https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html ngx_fastcgi].\n\nAlso, the Lua load balancer can work with the standard upstream connection pool mechanism,\ni.e., the standard [https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive keepalive] directive.\nJust ensure that the [https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive keepalive] directive\nis used *after* this <code>balancer_by_lua_block</code> directive in a single <code>upstream {}</code> configuration block.\n\nThe Lua load balancer can totally ignore the list of servers defined in the <code>upstream {}</code> block\nand select peer from a completely dynamic server list (even changing per request) via the\n[https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/balancer.md ngx.balancer] module\nfrom the [https://github.com/openresty/lua-resty-core lua-resty-core] library.\n\nThe Lua code handler registered by this directive might get called more than once in a single\ndownstream request when the Nginx upstream mechanism retries the request on conditions\nspecified by directives like the [https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream proxy_next_upstream]\ndirective.\n\nThis Lua code execution context does not support yielding, so Lua APIs that may yield\n(like cosockets and \"light threads\") are disabled in this context. One can usually work\naround this limitation by doing such operations in an earlier phase handler (like\n[[#access_by_lua|access_by_lua*]]) and passing along the result into this context\nvia the [[#ngx.ctx|ngx.ctx]] table.\n\nThis directive was first introduced in the <code>v0.10.0</code> release.\n\n== balancer_by_lua_file ==\n\n'''syntax:''' ''balancer_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''upstream''\n\n'''phase:''' ''content''\n\nEquivalent to [[#balancer_by_lua_block|balancer_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or, as from the <code>v0.5.0rc32</code> release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nThis directive was first introduced in the <code>v0.10.0</code> release.\n\n== balancer_keepalive ==\n\n'''syntax:''' ''balancer_keepalive <total-connections>''\n\n'''context:''' ''upstream''\n\n'''phase:''' ''loading-config''\n\nThe <code>total-connections</code> parameter sets the maximum number of idle\nkeepalive connections to upstream servers that are preserved in the cache of\neach worker process. When this number is exceeded, the least recently used\nconnections are closed.\n\nIt should be particularly noted that the keepalive directive does not limit the\ntotal number of connections to upstream servers that an nginx worker process\ncan open. The connections parameter should be set to a number small enough to\nlet upstream servers process new incoming connections as well.\n\nThis directive was first introduced in the <code>v0.10.21</code> release.\n\n== lua_need_request_body ==\n\n'''syntax:''' ''lua_need_request_body <on|off>''\n\n'''default:''' ''off''\n\n'''context:''' ''http, server, location, location if''\n\n'''phase:''' ''depends on usage''\n\nDetermines whether to force the request body data to be read before running rewrite/access/content_by_lua* or not. The Nginx core does not read the client request body by default and if request body data is required, then this directive should be turned <code>on</code> or the [[#ngx.req.read_body|ngx.req.read_body]] function should be called within the Lua code.\n\nTo read the request body data within the [[HttpCoreModule#$request_body|$request_body]] variable,\n[[HttpCoreModule#client_body_buffer_size|client_body_buffer_size]] must have the same value as [[HttpCoreModule#client_max_body_size|client_max_body_size]]. Because when the content length exceeds [[HttpCoreModule#client_body_buffer_size|client_body_buffer_size]] but less than [[HttpCoreModule#client_max_body_size|client_max_body_size]], Nginx will buffer the data into a temporary file on the disk, which will lead to empty value in the [[HttpCoreModule#$request_body|$request_body]] variable.\n\nIf the current location includes [[#rewrite_by_lua|rewrite_by_lua*]] directives,\nthen the request body will be read just before the [[#rewrite_by_lua|rewrite_by_lua*]] code is run (and also at the\n<code>rewrite</code> phase). Similarly, if only [[#content_by_lua|content_by_lua]] is specified,\nthe request body will not be read until the content handler's Lua code is\nabout to run (i.e., the request body will be read during the content phase).\n\nIt is recommended however, to use the [[#ngx.req.read_body|ngx.req.read_body]] and [[#ngx.req.discard_body|ngx.req.discard_body]] functions for finer control over the request body reading process instead.\n\nThis also applies to [[#access_by_lua|access_by_lua*]].\n\n== ssl_client_hello_by_lua_block ==\n\n'''syntax:''' ''ssl_client_hello_by_lua_block { lua-script }''\n\n'''context:''' ''http, server''\n\n'''phase:''' ''right-after-client-hello-message-was-processed''\n\nThis directive runs user Lua code when Nginx is about to post-process the SSL client hello message for the downstream\nSSL (https) connections.\n\nIt is particularly useful for dynamically setting the SSL protocols according to the SNI.\n\nIt is also useful to do some custom operations according to the per-connection information in the client hello message.\n\nFor example, one can parse custom client hello extension and do the corresponding handling in pure Lua.\n\nThis Lua handler will always run whether the SSL session is resumed (via SSL session IDs or TLS session tickets) or not.\nWhile the <code>ssl_certificate_by_lua*</code> Lua handler will only runs when initiating a full SSL handshake.\n\nThe [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md ngx.ssl.clienthello] Lua modules\nprovided by the [https://github.com/openresty/lua-resty-core/#readme lua-resty-core]\nlibrary are particularly useful in this context.\n\nNote that this handler runs in extremely early stage of SSL handshake, before the SSL client hello extensions are parsed.\nSo you can not use some Lua API like <code>ssl.server_name()</code> which is dependent on the later stage's processing.\n\nAlso note that only the directive in default server is valid for several virtual servers with the same IP address and port.\n\nBelow is a trivial example using the\n[https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md ngx.ssl.clienthello] module\nat the same time:\n\n<geshi lang=\"nginx\">\nserver {\n    listen 443 ssl;\n    server_name   test.com;\n    ssl_certificate /path/to/cert.crt;\n    ssl_certificate_key /path/to/key.key;\n    ssl_client_hello_by_lua_block {\n        local ssl_clt = require \"ngx.ssl.clienthello\"\n        local host, err = ssl_clt.get_client_hello_server_name()\n        if host == \"test.com\" then\n            ssl_clt.set_protocols({\"TLSv1\", \"TLSv1.1\"})\n        elseif host == \"test2.com\" then\n            ssl_clt.set_protocols({\"TLSv1.2\", \"TLSv1.3\"})\n        elseif not host then\n            ngx.log(ngx.ERR, \"failed to get the SNI name: \", err)\n            ngx.exit(ngx.ERROR)\n        else\n            ngx.log(ngx.ERR, \"unknown SNI name: \", host)\n            ngx.exit(ngx.ERROR)\n        end\n    }\n    ...\n}\nserver {\n    listen 443 ssl;\n    server_name   test2.com;\n    ssl_certificate /path/to/cert.crt;\n    ssl_certificate_key /path/to/key.key;\n    ...\n}\n</geshi>\n\nSee more information in the [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md ngx.ssl.clienthello]\nLua modules' official documentation.\n\nUncaught Lua exceptions in the user Lua code immediately abort the current SSL session, so does the\n[[#ngx.exit|ngx.exit]] call with an error code like <code>ngx.ERROR</code>.\n\nThis Lua code execution context *does* support yielding, so Lua APIs that may yield\n(like cosockets, sleeping, and \"light threads\")\nare enabled in this context\n\nNote, you need to configure the [https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate ssl_certificate]\nand [https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate_key ssl_certificate_key]\nto avoid the following error while starting NGINX:\n\n<geshi>\n    nginx: [emerg] no ssl configured for the server\n</geshi>\n\nThis directive requires OpenSSL 1.1.1 or greater.\n\nIf you are using the [official pre-built\npackages](https://openresty.org/en/linux-packages.html) for\n[OpenResty](https://openresty.org/) 1.21.4.1 or later, then everything should\nwork out of the box.\n\nIf you are not using the Nginx core shipped with\n[OpenResty](https://openresty.org) 1.21.4.1 or later, you will need to apply\npatches to the standard Nginx core:\n\nhttps://openresty.org/en/nginx-ssl-patches.html\n\nThis directive was first introduced in the <code>v0.10.21</code> release.\n\n== ssl_client_hello_by_lua_file ==\n\n'''syntax:''' ''ssl_client_hello_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''http, server''\n\n'''phase:''' ''right-after-client-hello-message-was-processed''\n\nEquivalent to [[#ssl_client_hello_by_lua_block|ssl_client_hello_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or, as from the <code>v0.5.0rc32</code> release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nThis directive was first introduced in the <code>v0.10.21</code> release.\n\n== ssl_certificate_by_lua_block ==\n\n'''syntax:''' ''ssl_certificate_by_lua_block { lua-script }''\n\n'''context:''' ''server''\n\n'''phase:''' ''right-before-SSL-handshake''\n\nThis directive runs user Lua code when Nginx is about to start the SSL handshake for the downstream\nSSL (https) connections.\n\nIt is particularly useful for setting the SSL certificate chain and the corresponding private key on a per-request\nbasis. It is also useful to load such handshake configurations nonblockingly from the remote (for example,\nwith the [[#ngx.socket.tcp|cosocket]] API). And one can also do per-request OCSP stapling handling in pure\nLua here as well.\n\nAnother typical use case is to do SSL handshake traffic control nonblockingly in this context,\nwith the help of the [https://github.com/openresty/lua-resty-limit-traffic lua-resty-limit-traffic#readme]\nlibrary, for example.\n\nOne can also do interesting things with the SSL handshake requests from the client side, like\nrejecting old SSL clients using the SSLv3 protocol or even below selectively.\n\nThe [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md ngx.ssl]\nand [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ocsp.md ngx.ocsp] Lua modules\nprovided by the [https://github.com/openresty/lua-resty-core/#readme lua-resty-core]\nlibrary are particularly useful in this context. You can use the Lua API offered by these two Lua modules\nto manipulate the SSL certificate chain and private key for the current SSL connection\nbeing initiated.\n\nThis Lua handler does not run at all, however, when Nginx/OpenSSL successfully resumes\nthe SSL session via SSL session IDs or TLS session tickets for the current SSL connection. In\nother words, this Lua handler only runs when Nginx has to initiate a full SSL handshake.\n\nBelow is a trivial example using the\n[https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md ngx.ssl] module\nat the same time:\n\n<geshi lang=\"nginx\">\n    server {\n        listen 443 ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            print(\"About to initiate a new SSL handshake!\")\n        }\n\n        location / {\n            root html;\n        }\n    }\n</geshi>\n\nSee more complicated examples in the [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md ngx.ssl]\nand [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ocsp.md ngx.ocsp]\nLua modules' official documentation.\n\nUncaught Lua exceptions in the user Lua code immediately abort the current SSL session, so does the\n[[#ngx.exit|ngx.exit]] call with an error code like <code>ngx.ERROR</code>.\n\nThis Lua code execution context *does* support yielding, so Lua APIs that may yield\n(like cosockets, sleeping, and \"light threads\")\nare enabled in this context.\n\nNote, however, you still need to configure the [https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate ssl_certificate] and\n[https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate_key ssl_certificate_key]\ndirectives even though you will not use this static certificate and private key at all. This is\nbecause the NGINX core requires their appearance otherwise you are seeing the following error\nwhile starting NGINX:\n\n<geshi>\n    nginx: [emerg] no ssl configured for the server\n</geshi>\n\nThis directive requires OpenSSL 1.0.2e or greater.\n\nIf you are using the [official pre-built\npackages](https://openresty.org/en/linux-packages.html) for\n[OpenResty](https://openresty.org/) 1.9.7.2 or later, then everything should\nwork out of the box.\n\nIf you are not using the Nginx core shipped with\n[OpenResty](https://openresty.org) 1.9.7.2 or later, you will need to apply\npatches to the standard Nginx core:\n\nhttps://openresty.org/en/nginx-ssl-patches.html\n\nThis directive was first introduced in the <code>v0.10.0</code> release.\n\n== ssl_certificate_by_lua_file ==\n\n'''syntax:''' ''ssl_certificate_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''server''\n\n'''phase:''' ''right-before-SSL-handshake''\n\nEquivalent to [[#ssl_certificate_by_lua_block|ssl_certificate_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or, as from the <code>v0.5.0rc32</code> release, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nThis directive was first introduced in the <code>v0.10.0</code> release.\n\n== ssl_session_fetch_by_lua_block ==\n\n'''syntax:''' ''ssl_session_fetch_by_lua_block { lua-script }''\n\n'''context:''' ''http''\n\n'''phase:''' ''right-before-SSL-handshake''\n\nThis directive runs Lua code to look up and load the SSL session (if any) according to the session ID\nprovided by the current SSL handshake request for the downstream.\n\nThe Lua API for obtaining the current session ID and loading a cached SSL session data\nis provided in the [ngx.ssl.session](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/session.md)\nLua module shipped with the [lua-resty-core](https://github.com/openresty/lua-resty-core#readme)\nlibrary.\n\nLua APIs that may yield, like [[#ngx.sleep|ngx.sleep]] and [[#ngx.socket.tcp|cosockets]],\nare enabled in this context.\n\nThis hook, together with the [[#ssl_session_store_by_lua_block|ssl_session_store_by_lua*]] hook,\ncan be used to implement distributed caching mechanisms in pure Lua (based\non the [[#ngx.socket.tcp|cosocket]] API, for example). If a cached SSL session is found\nand loaded into the current SSL connection context,\nSSL session resumption can then get immediately initiated and bypass the full SSL handshake process which is very expensive in terms of CPU time.\n\nPlease note that TLS session tickets are very different and it is the clients' responsibility\nto cache the SSL session state when session tickets are used. SSL session resumptions based on\nTLS session tickets would happen automatically without going through this hook (nor the\n[[#ssl_session_store_by_lua_block|ssl_session_store_by_lua*]] hook). This hook is mainly\nfor older or less capable SSL clients that can only do SSL sessions by session IDs.\n\nWhen [[#ssl_certificate_by_lua_block|ssl_certificate_by_lua*]] is specified at the same time,\nthis hook usually runs before [[#ssl_certificate_by_lua_block|ssl_certificate_by_lua*]].\nWhen the SSL session is found and successfully loaded for the current SSL connection,\nSSL session resumption will happen and thus bypass the [[#ssl_certificate_by_lua_block|ssl_certificate_by_lua*]]\nhook completely. In this case, Nginx also bypasses the [[#ssl_session_store_by_lua_block|ssl_session_store_by_lua*]]\nhook, for obvious reasons.\n\nTo easily test this hook locally with a modern web browser, you can temporarily put the following line\nin your https server block to disable the TLS session ticket support:\n\n    ssl_session_tickets off;\n\nBut do not forget to comment this line out before publishing your site to the world.\n\nIf you are using the [official pre-built packages](https://openresty.org/en/linux-packages.html) for [OpenResty](https://openresty.org/)\n1.11.2.1 or later, then everything should work out of the box.\n\nIf you are not using one of the [OpenSSL\npackages](https://openresty.org/en/linux-packages.html) provided by\n[OpenResty](https://openresty.org), you will need to apply patches to OpenSSL\nin order to use this directive:\n\nhttps://openresty.org/en/openssl-patches.html\n\nSimilarly, if you are not using the Nginx core shipped with\n[OpenResty](https://openresty.org) 1.11.2.1 or later, you will need to apply\npatches to the standard Nginx core:\n\nhttps://openresty.org/en/nginx-ssl-patches.html\n\nThis directive was first introduced in the <code>v0.10.6</code> release.\n\nNote that this directive can only be used in the '''http context''' starting\nwith the <code>v0.10.7</code> release since SSL session resumption happens\nbefore server name dispatch.\n\n== ssl_session_fetch_by_lua_file ==\n\n'''syntax:''' ''ssl_session_fetch_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''http''\n\n'''phase:''' ''right-before-SSL-handshake''\n\nEquivalent to [[#ssl_session_fetch_by_lua_block|ssl_session_fetch_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or rather, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nThis directive was first introduced in the <code>v0.10.6</code> release.\n\nNote that: this directive is only allowed to used in '''http context''' from the <code>v0.10.7</code> release\n(because SSL session resumption happens before server name dispatch).\n\n== ssl_session_store_by_lua_block ==\n\n'''syntax:''' ''ssl_session_store_by_lua_block { lua-script }''\n\n'''context:''' ''http''\n\n'''phase:''' ''right-after-SSL-handshake''\n\nThis directive runs Lua code to fetch and save the SSL session (if any) according to the session ID\nprovided by the current SSL handshake request for the downstream. The saved or cached SSL\nsession data can be used for future SSL connections to resume SSL sessions without going\nthrough the full SSL handshake process (which is very expensive in terms of CPU time).\n\nLua APIs that may yield, like [[#ngx.sleep|ngx.sleep]] and [[#ngx.socket.tcp|cosockets]],\nare *disabled* in this context. You can still, however, use the [[#ngx.timer.at|ngx.timer.at]] API\nto create 0-delay timers to save the SSL session data asynchronously to external services (like <code>redis</code> or <code>memcached</code>).\n\nThe Lua API for obtaining the current session ID and the associated session state data\nis provided in the [ngx.ssl.session](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/session.md#readme)\nLua module shipped with the [lua-resty-core](https://github.com/openresty/lua-resty-core#readme)\nlibrary.\n\nTo easily test this hook locally with a modern web browser, you can temporarily put the following line\nin your https server block to disable the TLS session ticket support:\n\n    ssl_session_tickets off;\n\nBut do not forget to comment this line out before publishing your site to the world.\n\nThis directive was first introduced in the <code>v0.10.6</code> release.\n\nNote that: this directive is only allowed to used in '''http context''' from the <code>v0.10.7</code> release\n(because SSL session resumption happens before server name dispatch).\n\n== ssl_session_store_by_lua_file ==\n\n'''syntax:''' ''ssl_session_store_by_lua_file <path-to-lua-script-file>''\n\n'''context:''' ''http''\n\n'''phase:''' ''right-after-SSL-handshake''\n\nEquivalent to [[#ssl_session_store_by_lua_block|ssl_session_store_by_lua_block]], except that the file specified by <code><path-to-lua-script-file></code> contains the Lua code, or rather, the [[#LuaJIT bytecode support|LuaJIT bytecode]] to be executed.\n\nWhen a relative path like <code>foo/bar.lua</code> is given, they will be turned into the absolute path relative to the <code>server prefix</code> path determined by the <code>-p PATH</code> command-line option while starting the Nginx server.\n\nThis directive was first introduced in the <code>v0.10.6</code> release.\n\nNote that: this directive is only allowed to used in '''http context''' from the <code>v0.10.7</code> release\n(because SSL session resumption happens before server name dispatch).\n\n== lua_shared_dict ==\n\n'''syntax:''' ''lua_shared_dict <name> <size>''\n\n'''default:''' ''no''\n\n'''context:''' ''http''\n\n'''phase:''' ''depends on usage''\n\nDeclares a shared memory zone, <code><name></code>, to serve as storage for the shm based Lua dictionary <code>ngx.shared.<name></code>.\n\nShared memory zones are always shared by all the Nginx worker processes in the current Nginx server instance.\n\nThe <code><size></code> argument accepts size units such as <code>k</code> and <code>m</code>:\n\n<geshi lang=\"nginx\">\n    http {\n        lua_shared_dict dogs 10m;\n        ...\n    }\n</geshi>\n\nThe hard-coded minimum size is 8KB while the practical minimum size depends\non actual user data set (some people start with 12KB).\n\nSee [[#ngx.shared.DICT|ngx.shared.DICT]] for details.\n\nThis directive was first introduced in the <code>v0.3.1rc22</code> release.\n\n== lua_socket_connect_timeout ==\n\n'''syntax:''' ''lua_socket_connect_timeout <time>''\n\n'''default:''' ''lua_socket_connect_timeout 60s''\n\n'''context:''' ''http, server, location''\n\nThis directive controls the default timeout value used in TCP/unix-domain socket object's [[#tcpsock:connect|connect]] method and can be overridden by the [[#tcpsock:settimeout|settimeout]] or [[#tcpsock:settimeouts|settimeouts]] methods.\n\nThe <code><time></code> argument can be an integer, with an optional time unit, like <code>s</code> (second), <code>ms</code> (millisecond), <code>m</code> (minute). The default time unit is <code>s</code>, i.e., \"second\". The default setting is <code>60s</code>.\n\nThis directive was first introduced in the <code>v0.5.0rc1</code> release.\n\n== lua_socket_send_timeout ==\n\n'''syntax:''' ''lua_socket_send_timeout <time>''\n\n'''default:''' ''lua_socket_send_timeout 60s''\n\n'''context:''' ''http, server, location''\n\nControls the default timeout value used in TCP/unix-domain socket object's [[#tcpsock:send|send]] method and can be overridden by the [[#tcpsock:settimeout|settimeout]] or [[#tcpsock:settimeouts|settimeouts]] methods.\n\nThe <code><time></code> argument can be an integer, with an optional time unit, like <code>s</code> (second), <code>ms</code> (millisecond), <code>m</code> (minute). The default time unit is <code>s</code>, i.e., \"second\". The default setting is <code>60s</code>.\n\nThis directive was first introduced in the <code>v0.5.0rc1</code> release.\n\n== lua_socket_send_lowat ==\n\n'''syntax:''' ''lua_socket_send_lowat <size>''\n\n'''default:''' ''lua_socket_send_lowat 0''\n\n'''context:''' ''http, server, location''\n\nControls the <code>lowat</code> (low water) value for the cosocket send buffer.\n\n== lua_socket_read_timeout ==\n\n'''syntax:''' ''lua_socket_read_timeout <time>''\n\n'''default:''' ''lua_socket_read_timeout 60s''\n\n'''context:''' ''http, server, location''\n\n'''phase:''' ''depends on usage''\n\nThis directive controls the default timeout value used in TCP/unix-domain socket object's [[#tcpsock:receive|receive]] method and iterator functions returned by the [[#tcpsock:receiveuntil|receiveuntil]] method. This setting can be overridden by the [[#tcpsock:settimeout|settimeout]] or [[#tcpsock:settimeouts|settimeouts]] methods.\n\nThe <code><time></code> argument can be an integer, with an optional time unit, like <code>s</code> (second), <code>ms</code> (millisecond), <code>m</code> (minute). The default time unit is <code>s</code>, i.e., \"second\". The default setting is <code>60s</code>.\n\nThis directive was first introduced in the <code>v0.5.0rc1</code> release.\n\n== lua_socket_buffer_size ==\n\n'''syntax:''' ''lua_socket_buffer_size <size>''\n\n'''default:''' ''lua_socket_buffer_size 4k/8k''\n\n'''context:''' ''http, server, location''\n\nSpecifies the buffer size used by cosocket reading operations.\n\nThis buffer does not have to be that big to hold everything at the same time because cosocket supports 100% non-buffered reading and parsing. So even <code>1</code> byte buffer size should still work everywhere but the performance could be terrible.\n\nThis directive was first introduced in the <code>v0.5.0rc1</code> release.\n\n== lua_socket_pool_size ==\n\n'''syntax:''' ''lua_socket_pool_size <size>''\n\n'''default:''' ''lua_socket_pool_size 30''\n\n'''context:''' ''http, server, location''\n\nSpecifies the size limit (in terms of connection count) for every cosocket connection pool associated with every remote server (i.e., identified by either the host-port pair or the unix domain socket file path).\n\nDefault to 30 connections for every pool.\n\nWhen the connection pool exceeds the available size limit, the least recently used (idle) connection already in the pool will be closed to make room for the current connection.\n\nNote that the cosocket connection pool is per Nginx worker process rather than per Nginx server instance, so size limit specified here also applies to every single Nginx worker process.\n\nThis directive was first introduced in the <code>v0.5.0rc1</code> release.\n\n== lua_socket_keepalive_timeout ==\n\n'''syntax:''' ''lua_socket_keepalive_timeout <time>''\n\n'''default:''' ''lua_socket_keepalive_timeout 60s''\n\n'''context:''' ''http, server, location''\n\nThis directive controls the default maximal idle time of the connections in the cosocket built-in connection pool. When this timeout reaches, idle connections will be closed and removed from the pool. This setting can be overridden by cosocket objects' [[#tcpsock:setkeepalive|setkeepalive]] method.\n\nThe <code><time></code> argument can be an integer, with an optional time unit, like <code>s</code> (second), <code>ms</code> (millisecond), <code>m</code> (minute). The default time unit is <code>s</code>, i.e., \"second\". The default setting is <code>60s</code>.\n\nThis directive was first introduced in the <code>v0.5.0rc1</code> release.\n\n== lua_socket_log_errors ==\n\n'''syntax:''' ''lua_socket_log_errors on|off''\n\n'''default:''' ''lua_socket_log_errors on''\n\n'''context:''' ''http, server, location''\n\nThis 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).\n\nThis directive was first introduced in the <code>v0.5.13</code> release.\n\n== lua_ssl_ciphers ==\n\n'''syntax:''' ''lua_ssl_ciphers <ciphers>''\n\n'''default:''' ''lua_ssl_ciphers DEFAULT''\n\n'''context:''' ''http, server, location''\n\nSpecifies the enabled ciphers for requests to a SSL/TLS server in the [[#tcpsock:sslhandshake|tcpsock:sslhandshake]] method. The ciphers are specified in the format understood by the OpenSSL library.\n\nThe full list can be viewed using the “openssl ciphers” command.\n\nThis directive was first introduced in the <code>v0.9.11</code> release.\n\n== lua_ssl_crl ==\n\n'''syntax:''' ''lua_ssl_crl <file>''\n\n'''default:''' ''no''\n\n'''context:''' ''http, server, location''\n\nSpecifies a file with revoked certificates (CRL) in the PEM format used to verify the certificate of the SSL/TLS server in the [[#tcpsock:sslhandshake|tcpsock:sslhandshake]] method.\n\nThis directive was first introduced in the <code>v0.9.11</code> release.\n\n== lua_ssl_protocols ==\n\n'''syntax:''' ''lua_ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3]''\n\n'''default:''' ''lua_ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2''\n\n'''context:''' ''http, server, location''\n\nEnables the specified protocols for requests to a SSL/TLS server in the [[#tcpsock:sslhandshake|tcpsock:sslhandshake]] method.\n\nThe support for the <code>TLSv1.3</code> parameter requires version <code>v0.10.12</code> *and* OpenSSL 1.1.1.\n\nThis directive was first introduced in the <code>v0.9.11</code> release.\n\n== lua_ssl_certificate ==\n\n'''syntax:''' ''lua_ssl_certificate <file>''\n\n'''default:''' ''none''\n\n'''context:''' ''http, server, location''\n\nSpecifies the file path to the SSL/TLS certificate in PEM format used for the [[#tcpsock:sslhandshake|tcpsock:sslhandshake]] method.\n\nThis directive allows you to specify the SSL/TLS certificate that will be presented to server during the SSL/TLS handshake process.\n\nThis directive was first introduced in the <code>v0.10.26</code> release.\n\nSee also [[#lua_ssl_certificate_key|lua_ssl_certificate_key]] and [[#lua_ssl_verify_depth|lua_ssl_verify_depth]].\n\n== lua_ssl_certificate_key ==\n\n'''syntax:''' ''lua_ssl_certificate_key <file>''\n\n'''default:''' ''none''\n\n'''context:''' ''http, server, location''\n\nSpecifies the file path to the private key associated with the SSL/TLS certificate used in the [[#tcpsock:sslhandshake|tcpsock:sslhandshake]] method.\n\nThis 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.\n\nThis directive was first introduced in the <code>v0.10.26</code> release.\n\nSee also [[#lua_ssl_certificate|lua_ssl_certificate]] and [[#lua_ssl_verify_depth|lua_ssl_verify_depth]].\n\n== lua_ssl_trusted_certificate ==\n\n'''syntax:''' ''lua_ssl_trusted_certificate <file>''\n\n'''default:''' ''none''\n\n'''context:''' ''http, server, location''\n\nSpecifies 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|tcpsock:sslhandshake]] method.\n\nThis directive was first introduced in the <code>v0.9.11</code> release.\n\nSee also [[#lua_ssl_verify_depth|lua_ssl_verify_depth]].\n\n== lua_ssl_verify_depth ==\n\n'''syntax:''' ''lua_ssl_verify_depth <number>''\n\n'''default:''' ''lua_ssl_verify_depth 1''\n\n'''context:''' ''http, server, location''\n\nSets the verification depth in the server certificates chain.\n\nThis directive was first introduced in the <code>v0.9.11</code> release.\n\nSee also [[#lua_ssl_certificate|lua_ssl_certificate]], [[#lua_ssl_certificate_key|lua_ssl_certificate_key]] and [[#lua_ssl_trusted_certificate|lua_ssl_trusted_certificate]].\n\n== lua_ssl_key_log ==\n\n'''syntax:''' ''lua_ssl_key_log <file>''\n\n'''default:''' ''none''\n\n'''context:''' ''http, server, location''\n\nEnables 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.\n\n== lua_ssl_conf_command ==\n\n'''syntax:''' ''lua_ssl_conf_command <command>''\n\n'''default:''' ''no''\n\n'''context:''' ''http, server, location''\n\nSets arbitrary OpenSSL configuration [https://www.openssl.org/docs/man1.1.1/man3/SSL_CONF_cmd.html commands].\n\nThe 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.\n\nSeveral <code>lua_ssl_conf_command</code> directives can be specified on the same level:\n\n<geshi lang=\"nginx\">\nlua_ssl_conf_command Options PrioritizeChaCha;\nlua_ssl_conf_command Ciphersuites TLS_CHACHA20_POLY1305_SHA256;\n</geshi>\n\nConfiguration commands are applied after OpenResty own configuration for SSL, so they can be used to override anything set by OpenResty.\n\nNote though that configuring OpenSSL directly with <code>lua_ssl_conf_command</code> might result in a behaviour OpenResty does not expect, and should be done with care.\n\nThis directive was first introduced in the <code>v0.10.21</code> release.\n\n== lua_upstream_skip_openssl_default_verify ==\n\n'''syntax:''' ''lua_upstream_skip_openssl_default_verify on|off''\n\n'''default:''' ''lua_upstream_skip_openssl_default_verify off''\n\n'''context:''' ''location, location-if''\n\nWhen 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.\n\nThis directive is turned <code>off</code> by default.\n\n[Back to TOC](#directives)\n\n\n== lua_http10_buffering ==\n\n'''syntax:''' ''lua_http10_buffering on|off''\n\n'''default:''' ''lua_http10_buffering on''\n\n'''context:''' ''http, server, location, location-if''\n\nEnables or disables automatic response buffering for HTTP 1.0 (or older) requests. This buffering mechanism is mainly used for HTTP 1.0 keep-alive which relies on a proper <code>Content-Length</code> response header.\n\nIf the Lua code explicitly sets a <code>Content-Length</code> response header before sending the headers (either explicitly via [[#ngx.send_headers|ngx.send_headers]] or implicitly via the first [[#ngx.say|ngx.say]] or [[#ngx.print|ngx.print]] call), then the HTTP 1.0 response buffering will be disabled even when this directive is turned on.\n\nTo output very large response data in a streaming fashion (via the [[#ngx.flush|ngx.flush]] call, for example), this directive MUST be turned off to minimize memory usage.\n\nThis directive is turned <code>on</code> by default.\n\nThis directive was first introduced in the <code>v0.5.0rc19</code> release.\n\n== rewrite_by_lua_no_postpone ==\n\n'''syntax:''' ''rewrite_by_lua_no_postpone on|off''\n\n'''default:''' ''rewrite_by_lua_no_postpone off''\n\n'''context:''' ''http''\n\nControls whether or not to disable postponing [[#rewrite_by_lua|rewrite_by_lua*]] directives to run at the end of the <code>rewrite</code> request-processing phase. By default, this directive is turned off and the Lua code is postponed to run at the end of the <code>rewrite</code> phase.\n\nThis directive was first introduced in the <code>v0.5.0rc29</code> release.\n\n== access_by_lua_no_postpone ==\n\n'''syntax:''' ''access_by_lua_no_postpone on|off''\n\n'''default:''' ''access_by_lua_no_postpone off''\n\n'''context:''' ''http''\n\nControls whether or not to disable postponing [[#access_by_lua|access_by_lua*]] directives to run at the end of the <code>access</code> request-processing phase. By default, this directive is turned off and the Lua code is postponed to run at the end of the <code>access</code> phase.\n\nThis directive was first introduced in the <code>v0.9.20</code> release.\n\n== precontent_by_lua_no_postpone ==\n\n'''syntax:''' ''precontent_by_lua_no_postpone on|off''\n\n'''default:''' ''precontent_by_lua_no_postpone off''\n\n'''context:''' ''http''\n\nControls whether or not to disable postponing [[#precontent_by_lua_block|precontent_by_lua_block*]] directives to run at the end of the <code>precontent</code> request-processing phase. By default, this directive is turned off and the Lua code is postponed to run at the end of the <code>precontent</code> phase.\n\n== lua_transform_underscores_in_response_headers ==\n\n'''syntax:''' ''lua_transform_underscores_in_response_headers on|off''\n\n'''default:''' ''lua_transform_underscores_in_response_headers on''\n\n'''context:''' ''http, server, location, location-if''\n\nControls whether to transform underscores (<code>_</code>) in the response header names specified in the [[#ngx.header.HEADER|ngx.header.HEADER]] API to hyphens (<code>-</code>).\n\nThis directive was first introduced in the <code>v0.5.0rc32</code> release.\n\n== lua_check_client_abort ==\n\n'''syntax:''' ''lua_check_client_abort on|off''\n\n'''default:''' ''lua_check_client_abort off''\n\n'''context:''' ''http, server, location, location-if''\n\nThis directive controls whether to check for premature client connection abortion.\n\nWhen this directive is on, the ngx_lua module will monitor the premature connection close event on the downstream connections and when there is such an event, it will call the user Lua function callback (registered by [[#ngx.on_abort|ngx.on_abort]]) or just stop and clean up all the Lua \"light threads\" running in the current request's request handler when there is no user callback function registered.\n\nAccording to the current implementation, however, if the client closes the connection before the Lua code finishes reading the request body data via [[#ngx.req.socket|ngx.req.socket]], then ngx_lua will neither stop all the running \"light threads\" nor call the user callback (if [[#ngx.on_abort|ngx.on_abort]] has been called). Instead, the reading operation on [[#ngx.req.socket|ngx.req.socket]] will just return the error message \"client aborted\" as the second return value (the first return value is surely <code>nil</code>).\n\nWhen TCP keepalive is disabled, it is relying on the client side to close the socket gracefully (by sending a <code>FIN</code> packet or something like that). For (soft) real-time web applications, it is highly recommended to configure the [http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html TCP keepalive] support in your system's TCP stack implementation in order to detect \"half-open\" TCP connections in time.\n\nFor example, on Linux, you can configure the standard [[HttpCoreModule#listen|listen]] directive in your <code>nginx.conf</code> file like this:\n\n<geshi lang=\"nginx\">\n    listen 80 so_keepalive=2s:2s:8;\n</geshi>\n\nOn FreeBSD, you can only tune the system-wide configuration for TCP keepalive, for example:\n\n    # sysctl net.inet.tcp.keepintvl=2000\n    # sysctl net.inet.tcp.keepidle=2000\n\nThis directive was first introduced in the <code>v0.7.4</code> release.\n\nSee also [[#ngx.on_abort|ngx.on_abort]].\n\n== lua_max_pending_timers ==\n\n'''syntax:''' ''lua_max_pending_timers <count>''\n\n'''default:''' ''lua_max_pending_timers 1024''\n\n'''context:''' ''http''\n\nControls the maximum number of pending timers allowed.\n\nPending timers are those timers that have not expired yet.\n\nWhen exceeding this limit, the [[#ngx.timer.at|ngx.timer.at]] call will immediately return <code>nil</code> and the error string \"too many pending timers\".\n\nThis directive was first introduced in the <code>v0.8.0</code> release.\n\n== lua_max_running_timers ==\n\n'''syntax:''' ''lua_max_running_timers <count>''\n\n'''default:''' ''lua_max_running_timers 256''\n\n'''context:''' ''http''\n\nControls the maximum number of \"running timers\" allowed.\n\nRunning timers are those timers whose user callback functions are still running.\n\nWhen 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.\n\nThis directive was first introduced in the <code>v0.8.0</code> release.\n\n== lua_sa_restart ==\n\n'''syntax:''' ''lua_sa_restart on|off''\n\n'''default:''' ''lua_sa_restart on''\n\n'''context:''' ''http''\n\nWhen enabled, this module will set the `SA_RESTART` flag on Nginx workers signal dispositions.\n\nThis allows Lua I/O primitives to not be interrupted by Nginx's handling of various signals.\n\nThis directive was first introduced in the <code>v0.10.14</code> release.\n\n== lua_worker_thread_vm_pool_size ==\n\n'''syntax:''' ''lua_worker_thread_vm_pool_size <size>''\n\n'''default:''' ''lua_worker_thread_vm_pool_size 100''\n\n'''context:''' ''http''\n\nSpecifies the size limit of the Lua VM pool (default 100) that will be used in the [ngx.run_worker_thread](#ngxrun_worker_thread) API.\n\nAlso, it is not allowed to create Lua VMs that exceeds the pool size limit.\n\nThe Lua VM in the VM pool is used to execute Lua code in separate thread.\n\nThe pool is global at Nginx worker level. And it is used to reuse Lua VMs between requests.\n\n= Nginx API for Lua =\n\n<!-- inline-toc -->\n\n== Introduction ==\n\nThe various <code>*_by_lua</code>, <code>*_by_lua_block</code> and <code>*_by_lua_file</code> configuration directives serve as gateways to the Lua API within the <code>nginx.conf</code> file. The Nginx Lua API described below can only be called within the user Lua code run in the context of these configuration directives.\n\nThe API is exposed to Lua in the form of two standard packages <code>ngx</code> and <code>ndk</code>. These packages are in the default global scope within ngx_lua and are always available within ngx_lua directives.\n\nThe packages can be introduced into external Lua modules like this:\n\n<geshi lang=\"lua\">\n    local say = ngx.say\n\n    local _M = {}\n\n    function _M.foo(a)\n        say(a)\n    end\n\n    return _M\n</geshi>\n\nUse of the [https://www.lua.org/manual/5.1/manual.html#pdf-package.seeall package.seeall] flag is strongly discouraged due to its various bad side-effects.\n\nIt is also possible to directly require the packages in external Lua modules:\n\n<geshi lang=\"lua\">\n    local ngx = require \"ngx\"\n    local ndk = require \"ndk\"\n</geshi>\n\nThe ability to require these packages was introduced in the <code>v0.2.1rc19</code> release.\n\nNetwork I/O operations in user code should only be done through the Nginx Lua API calls as the Nginx event loop may be blocked and performance drop off dramatically otherwise. Disk operations with relatively small amount of data can be done using the standard Lua <code>io</code> library but huge file reading and writing should be avoided wherever possible as they may block the Nginx process significantly. Delegating all network and disk I/O operations to Nginx's subrequests (via the [[#ngx.location.capture|ngx.location.capture]] method and similar) is strongly recommended for maximum performance.\n\n== ngx.arg ==\n\n'''syntax:''' ''val = ngx.arg[index]''\n\n'''context:''' ''set_by_lua*, body_filter_by_lua*''\n\nWhen this is used in the context of the [[#set_by_lua|set_by_lua*]] directives, this table is read-only and holds the input arguments to the config directives:\n\n<geshi lang=\"lua\">\n    value = ngx.arg[n]\n</geshi>\n\nHere is an example\n\n<geshi lang=\"nginx\">\n    location /foo {\n        set $a 32;\n        set $b 56;\n\n        set_by_lua $sum\n            'return tonumber(ngx.arg[1]) + tonumber(ngx.arg[2])'\n            $a $b;\n\n        echo $sum;\n    }\n</geshi>\n\nthat writes out <code>88</code>, the sum of <code>32</code> and <code>56</code>.\n\nWhen this table is used in the context of [[#body_filter_by_lua|body_filter_by_lua*]], the first element holds the input data chunk to the output filter code and the second element holds the boolean flag for the \"eof\" flag indicating the end of the whole output data stream.\n\nThe data chunk and \"eof\" flag passed to the downstream Nginx output filters can also be overridden by assigning values directly to the corresponding table elements. When setting <code>nil</code> or an empty Lua string value to <code>ngx.arg[1]</code>, no data chunk will be passed to the downstream Nginx output filters at all.\n\n== ngx.var.VARIABLE ==\n\n'''syntax:''' ''ngx.var.VAR_NAME''\n\n'''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*''\n\nRead and write Nginx variable values.\n\n<geshi lang=\"nginx\">\n    value = ngx.var.some_nginx_variable_name\n    ngx.var.some_nginx_variable_name = value\n</geshi>\n\nNote that only already defined Nginx variables can be written to.\nFor example:\n\n<geshi lang=\"nginx\">\n    location /foo {\n        set $my_var ''; # this line is required to create $my_var at config time\n        content_by_lua_block {\n            ngx.var.my_var = 123\n            ...\n        }\n    }\n</geshi>\n\nThat is, Nginx variables cannot be created on-the-fly.\n\nSome special Nginx variables like <code>$args</code> and <code>$limit_rate</code> can be assigned a value,\nmany others are not, like <code>$query_string</code>, <code>$arg_PARAMETER</code>, and <code>$http_NAME</code>.\n\nNginx regex group capturing variables <code>$1</code>, <code>$2</code>, <code>$3</code>, and etc, can be read by this\ninterface as well, by writing <code>ngx.var[1]</code>, <code>ngx.var[2]</code>, <code>ngx.var[3]</code>, and etc.\n\nSetting <code>ngx.var.Foo</code> to a <code>nil</code> value will unset the <code>$Foo</code> Nginx variable.\n\n<geshi lang=\"lua\">\n    ngx.var.args = nil\n</geshi>\n\n'''CAUTION''' When reading from an Nginx variable, Nginx will allocate memory in the per-request memory pool which is freed only at request termination. So when you need to read from an Nginx variable repeatedly in your Lua code, cache the Nginx variable value to your own Lua variable, for example,\n\n<geshi lang=\"lua\">\n    local val = ngx.var.some_var\n    --- use the val repeatedly later\n</geshi>\n\nto prevent (temporary) memory leaking within the current request's lifetime. Another way of caching the result is to use the [[#ngx.ctx|ngx.ctx]] table.\n\nUndefined Nginx variables are evaluated to <code>nil</code> while uninitialized (but defined) Nginx variables are evaluated to an empty Lua string.\n\nThis API requires a relatively expensive metamethod call and it is recommended to avoid using it on hot code paths.\n\n== Core constants ==\n\n'''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*''\n\n<geshi lang=\"lua\">\n  ngx.OK (0)\n  ngx.ERROR (-1)\n  ngx.AGAIN (-2)\n  ngx.DONE (-4)\n  ngx.DECLINED (-5)\n</geshi>\n\nNote that only three of these constants are utilized by the [[#Nginx API for Lua|Nginx API for Lua]] (i.e., [[#ngx.exit|ngx.exit]] accepts <code>ngx.OK</code>, <code>ngx.ERROR</code>, and <code>ngx.DECLINED</code> as input).\n\n<geshi lang=\"lua\">\n  ngx.null\n</geshi>\n\nThe <code>ngx.null</code> constant is a <code>NULL</code> light userdata usually used to represent nil values in Lua tables etc and is similar to the [http://www.kyne.com.au/~mark/software/lua-cjson.php lua-cjson] library's <code>cjson.null</code> constant. This constant was first introduced in the <code>v0.5.0rc5</code> release.\n\nThe <code>ngx.DECLINED</code> constant was first introduced in the <code>v0.5.0rc19</code> release.\n\n== HTTP method constants ==\n\n'''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*''\n\n<geshi lang=\"text\">\n  ngx.HTTP_GET\n  ngx.HTTP_HEAD\n  ngx.HTTP_PUT\n  ngx.HTTP_POST\n  ngx.HTTP_DELETE\n  ngx.HTTP_OPTIONS   (added in the v0.5.0rc24 release)\n  ngx.HTTP_MKCOL     (added in the v0.8.2 release)\n  ngx.HTTP_COPY      (added in the v0.8.2 release)\n  ngx.HTTP_MOVE      (added in the v0.8.2 release)\n  ngx.HTTP_PROPFIND  (added in the v0.8.2 release)\n  ngx.HTTP_PROPPATCH (added in the v0.8.2 release)\n  ngx.HTTP_LOCK      (added in the v0.8.2 release)\n  ngx.HTTP_UNLOCK    (added in the v0.8.2 release)\n  ngx.HTTP_PATCH     (added in the v0.8.2 release)\n  ngx.HTTP_TRACE     (added in the v0.8.2 release)\n</geshi>\n\nThese constants are usually used in [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]] method calls.\n\n== HTTP status constants ==\n\n'''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*''\n\n<geshi lang=\"nginx\">\n  value = ngx.HTTP_CONTINUE (100) (first added in the v0.9.20 release)\n  value = ngx.HTTP_SWITCHING_PROTOCOLS (101) (first added in the v0.9.20 release)\n  value = ngx.HTTP_OK (200)\n  value = ngx.HTTP_CREATED (201)\n  value = ngx.HTTP_ACCEPTED (202) (first added in the v0.9.20 release)\n  value = ngx.HTTP_NO_CONTENT (204) (first added in the v0.9.20 release)\n  value = ngx.HTTP_PARTIAL_CONTENT (206) (first added in the v0.9.20 release)\n  value = ngx.HTTP_SPECIAL_RESPONSE (300)\n  value = ngx.HTTP_MOVED_PERMANENTLY (301)\n  value = ngx.HTTP_MOVED_TEMPORARILY (302)\n  value = ngx.HTTP_SEE_OTHER (303)\n  value = ngx.HTTP_NOT_MODIFIED (304)\n  value = ngx.HTTP_TEMPORARY_REDIRECT (307) (first added in the v0.9.20 release)\n  value = ngx.HTTP_PERMANENT_REDIRECT (308)\n  value = ngx.HTTP_BAD_REQUEST (400)\n  value = ngx.HTTP_UNAUTHORIZED (401)\n  value = ngx.HTTP_PAYMENT_REQUIRED (402) (first added in the v0.9.20 release)\n  value = ngx.HTTP_FORBIDDEN (403)\n  value = ngx.HTTP_NOT_FOUND (404)\n  value = ngx.HTTP_NOT_ALLOWED (405)\n  value = ngx.HTTP_NOT_ACCEPTABLE (406) (first added in the v0.9.20 release)\n  value = ngx.HTTP_REQUEST_TIMEOUT (408) (first added in the v0.9.20 release)\n  value = ngx.HTTP_CONFLICT (409) (first added in the v0.9.20 release)\n  value = ngx.HTTP_GONE (410)\n  value = ngx.HTTP_UPGRADE_REQUIRED (426) (first added in the v0.9.20 release)\n  value = ngx.HTTP_TOO_MANY_REQUESTS (429) (first added in the v0.9.20 release)\n  value = ngx.HTTP_CLOSE (444) (first added in the v0.9.20 release)\n  value = ngx.HTTP_ILLEGAL (451) (first added in the v0.9.20 release)\n  value = ngx.HTTP_INTERNAL_SERVER_ERROR (500)\n  value = ngx.HTTP_NOT_IMPLEMENTED (501)\n  value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) (kept for compatibility)\n  value = ngx.HTTP_BAD_GATEWAY (502) (first added in the v0.9.20 release)\n  value = ngx.HTTP_SERVICE_UNAVAILABLE (503)\n  value = ngx.HTTP_GATEWAY_TIMEOUT (504) (first added in the v0.3.1rc38 release)\n  value = ngx.HTTP_VERSION_NOT_SUPPORTED (505) (first added in the v0.9.20 release)\n  value = ngx.HTTP_INSUFFICIENT_STORAGE (507) (first added in the v0.9.20 release)\n</geshi>\n\n== Nginx log level constants ==\n\n'''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*''\n\n<geshi lang=\"lua\">\n  ngx.STDERR\n  ngx.EMERG\n  ngx.ALERT\n  ngx.CRIT\n  ngx.ERR\n  ngx.WARN\n  ngx.NOTICE\n  ngx.INFO\n  ngx.DEBUG\n</geshi>\n\nThese constants are usually used by the [[#ngx.log|ngx.log]] method.\n\n== print ==\n\n'''syntax:''' ''print(...)''\n\n'''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*''\n\nWrites argument values into the Nginx <code>error.log</code> file with the <code>ngx.NOTICE</code> log level.\n\nIt is equivalent to\n\n<geshi lang=\"lua\">\n    ngx.log(ngx.NOTICE, ...)\n</geshi>\n\nLua <code>nil</code> arguments are accepted and result in literal <code>\"nil\"</code> strings while Lua booleans result in literal <code>\"true\"</code> or <code>\"false\"</code> strings. And the <code>ngx.null</code> constant will yield the <code>\"null\"</code> string output.\n\nThere is a hard coded <code>2048</code> byte limitation on error message lengths in the Nginx core. This limit includes trailing newlines and leading time stamps. If the message size exceeds this limit, Nginx will truncate the message text accordingly. This limit can be manually modified by editing the <code>NGX_MAX_ERROR_STR</code> macro definition in the <code>src/core/ngx_log.h</code> file in the Nginx source tree.\n\n== ngx.ctx ==\n\n'''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*, exit_worker_by_lua*''\n\nThis table can be used to store per-request Lua context data and has a life time identical to the current request (as with the Nginx variables).\n\nConsider the following example,\n\n<geshi lang=\"nginx\">\n    location /test {\n        rewrite_by_lua_block {\n            ngx.ctx.foo = 76\n        }\n        access_by_lua_block {\n            ngx.ctx.foo = ngx.ctx.foo + 3\n        }\n        content_by_lua_block {\n            ngx.say(ngx.ctx.foo)\n        }\n    }\n</geshi>\n\nThen <code>GET /test</code> will yield the output\n\n<geshi lang=\"bash\">\n    79\n</geshi>\n\nThat is, the <code>ngx.ctx.foo</code> entry persists across the rewrite, access, and content phases of a request.\n\nEvery request, including subrequests, has its own copy of the table. For example:\n\n<geshi lang=\"nginx\">\n    location /sub {\n        content_by_lua_block {\n            ngx.say(\"sub pre: \", ngx.ctx.blah)\n            ngx.ctx.blah = 32\n            ngx.say(\"sub post: \", ngx.ctx.blah)\n        }\n    }\n\n    location /main {\n        content_by_lua_block {\n            ngx.ctx.blah = 73\n            ngx.say(\"main pre: \", ngx.ctx.blah)\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n            ngx.say(\"main post: \", ngx.ctx.blah)\n        }\n    }\n</geshi>\n\nThen <code>GET /main</code> will give the output\n\n<geshi lang=\"bash\">\n    main pre: 73\n    sub pre: nil\n    sub post: 32\n    main post: 73\n</geshi>\n\nHere, modification of the <code>ngx.ctx.blah</code> entry in the subrequest does not affect the one in the parent request. This is because they have two separate versions of <code>ngx.ctx.blah</code>.\n\nInternal redirects (triggered by nginx configuration directives like `error_page`, `try_files`, `index` and etc) will destroy the original request <code>ngx.ctx</code> data (if any) and the new request will have an empty <code>ngx.ctx</code> table. For instance,\n\n<geshi lang=\"nginx\">\n    location /new {\n        content_by_lua_block {\n            ngx.say(ngx.ctx.foo)\n        }\n    }\n\n    location /orig {\n        content_by_lua_block {\n            ngx.ctx.foo = \"hello\"\n            ngx.exec(\"/new\")\n        }\n    }\n</geshi>\n\nThen <code>GET /orig</code> will give\n\n<geshi lang=\"bash\">\n    nil\n</geshi>\n\nrather than the original <code>\"hello\"</code> value.\n\nBecause HTTP request is created after SSL handshake, the <code>ngx.ctx</code> created\nin [[#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*]]\nis not available in the following phases like [[#rewrite_by_lua|rewrite_by_lua*]].\n\nSince <code>v0.10.18</code>, the <code>ngx.ctx</code> created during a SSL handshake\nwill be inherited by the requests which share the same TCP connection established by the handshake.\nNote that overwrite values in <code>ngx.ctx</code> in the http request phases (like `rewrite_by_lua*`) will only take affect in the current http request.\n\nArbitrary data values, including Lua closures and nested tables, can be inserted into this \"magic\" table. It also allows the registration of custom meta methods.\n\nOverriding <code>ngx.ctx</code> with a new Lua table is also supported, for example,\n\n<geshi lang=\"lua\">\n    ngx.ctx = { foo = 32, bar = 54 }\n</geshi>\n\nWhen being used in the context of [[#init_worker_by_lua|init_worker_by_lua*]], this table just has the same lifetime of the current Lua handler.\n\nThe <code>ngx.ctx</code> lookup requires relatively expensive metamethod calls and it is much slower than explicitly passing per-request data along by your own function arguments. So do not abuse this API for saving your own function arguments because it usually has quite some performance impact.\n\nBecause of the metamethod magic, never \"local\" the <code>ngx.ctx</code> table outside your Lua function scope on the Lua module level due to [[#Data_Sharing_within_an_Nginx_Worker|worker-level data sharing]]. For example, the following is bad:\n\n<geshi lang=\"lua\">\n-- mymodule.lua\nlocal _M = {}\n\n-- the following line is bad since ngx.ctx is a per-request\n-- data while this <code>ctx</code> variable is on the Lua module level\n-- and thus is per-nginx-worker.\nlocal ctx = ngx.ctx\n\nfunction _M.main()\n    ctx.foo = \"bar\"\nend\n\nreturn _M\n</geshi>\n\nUse the following instead:\n\n<geshi lang=\"lua\">\n-- mymodule.lua\nlocal _M = {}\n\nfunction _M.main(ctx)\n    ctx.foo = \"bar\"\nend\n\nreturn _M\n</geshi>\n\nThat is, let the caller pass the <code>ctx</code> table explicitly via a function argument.\n\n== ngx.location.capture ==\n\n'''syntax:''' ''res = ngx.location.capture(uri, options?)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nIssues a synchronous but still non-blocking ''Nginx Subrequest'' using <code>uri</code>.\n\nNginx's subrequests provide a powerful way to make non-blocking internal requests to other locations configured with disk file directory or ''any'' other Nginx C modules like <code>ngx_proxy</code>, <code>ngx_fastcgi</code>, <code>ngx_memc</code>,\n<code>ngx_postgres</code>, <code>ngx_drizzle</code>, and even ngx_lua itself and etc etc etc.\n\nAlso note that subrequests just mimic the HTTP interface but there is ''no'' extra HTTP/TCP traffic ''nor'' IPC involved. Everything works internally, efficiently, on the C level.\n\nSubrequests are completely different from HTTP 301/302 redirection (via [[#ngx.redirect|ngx.redirect]]) and internal redirection (via [[#ngx.exec|ngx.exec]]).\n\nYou should always read the request body (by either calling [[#ngx.req.read_body|ngx.req.read_body]] or configuring [[#lua_need_request_body|lua_need_request_body]] on) before initiating a subrequest.\n\nThis API function (as well as [[#ngx.location.capture_multi|ngx.location.capture_multi]]) always buffers the whole response body of the subrequest in memory. Thus, you should use [[#ngx.socket.tcp|cosockets]]\nand streaming processing instead if you have to handle large subrequest responses.\n\nHere is a basic example:\n\n<geshi lang=\"lua\">\n    res = ngx.location.capture(uri)\n</geshi>\n\nReturns a Lua table with 4 slots: <code>res.status</code>, <code>res.header</code>, <code>res.body</code>, and <code>res.truncated</code>.\n\n<code>res.status</code> holds the response status code for the subrequest response.\n\n<code>res.header</code> holds all the response headers of the\nsubrequest and it is a normal Lua table. For multi-value response headers,\nthe value is a Lua (array) table that holds all the values in the order that\nthey appear. For instance, if the subrequest response headers contain the following\nlines:\n\n<geshi lang=\"bash\">\n    Set-Cookie: a=3\n    Set-Cookie: foo=bar\n    Set-Cookie: baz=blah\n</geshi>\n\nThen <code>res.header[\"Set-Cookie\"]</code> will be evaluated to the table value\n<code>{\"a=3\", \"foo=bar\", \"baz=blah\"}</code>.\n\n<code>res.body</code> holds the subrequest's response body data, which might be truncated. You always need to check the <code>res.truncated</code> boolean flag to see if <code>res.body</code> contains truncated data. The data truncation here can only be caused by those unrecoverable errors in your subrequests like the cases that the remote end aborts the connection prematurely in the middle of the response body data stream or a read timeout happens when your subrequest is receiving the response body data from the remote.\n\nURI query strings can be concatenated to URI itself, for instance,\n\n<geshi lang=\"lua\">\n    res = ngx.location.capture('/foo/bar?a=3&b=4')\n</geshi>\n\nNamed locations like <code>@foo</code> are not allowed due to a limitation in\nthe Nginx core. Use normal locations combined with the <code>internal</code> directive to\nprepare internal-only locations.\n\nAn optional option table can be fed as the second\nargument, which supports the options:\n\n* <code>method</code>\n: specify the subrequest's request method, which only accepts constants like <code>ngx.HTTP_POST</code>.\n* <code>body</code>\n: specify the subrequest's request body (string value only).\n* <code>args</code>\n: specify the subrequest's URI query arguments (both string value and Lua tables are accepted)\n* <code>ctx</code>\n: specify a Lua table to be the [[#ngx.ctx|ngx.ctx]] table for the subrequest. It can be the current request's [[#ngx.ctx|ngx.ctx]] table, which effectively makes the parent and its subrequest to share exactly the same context table. This option was first introduced in the <code>v0.3.1rc25</code> release.\n* <code>vars</code>\n: take a Lua table which holds the values to set the specified Nginx variables in the subrequest as this option's value. This option was first introduced in the <code>v0.3.1rc31</code> release.\n* <code>copy_all_vars</code>\n: specify whether to copy over all the Nginx variable values of the current request to the subrequest in question. modifications of the Nginx variables in the subrequest will not affect the current (parent) request. This option was first introduced in the <code>v0.3.1rc31</code> release.\n* <code>share_all_vars</code>\n: specify whether to share all the Nginx variables of the subrequest with the current (parent) request. modifications of the Nginx variables in the subrequest will affect the current (parent) request. Enabling this option may lead to hard-to-debug issues due to bad side-effects and is considered bad and harmful. Only enable this option when you completely know what you are doing.\n* <code>always_forward_body</code>\n: when set to true, the current (parent) request's request body will always be forwarded to the subrequest being created if the <code>body</code> option is not specified. The request body read by either [[#ngx.req.read_body|ngx.req.read_body()]] or [[#lua_need_request_body|lua_need_request_body on]] will be directly forwarded to the subrequest without copying the whole request body data when creating the subrequest (no matter the request body data is buffered in memory buffers or temporary files). By default, this option is <code>false</code> and when the <code>body</code> option is not specified, the request body of the current (parent) request is only forwarded when the subrequest takes the <code>PUT</code> or <code>POST</code> request method.\n\nIssuing a POST subrequest, for example, can be done as follows\n\n<geshi lang=\"lua\">\n    res = ngx.location.capture(\n        '/foo/bar',\n        { method = ngx.HTTP_POST, body = 'hello, world' }\n    )\n</geshi>\n\nSee HTTP method constants methods other than POST.\nThe <code>method</code> option is <code>ngx.HTTP_GET</code> by default.\n\nThe <code>args</code> option can specify extra URI arguments, for instance,\n\n<geshi lang=\"lua\">\n    ngx.location.capture('/foo?a=1',\n        { args = { b = 3, c = ':' } }\n    )\n</geshi>\n\nis equivalent to\n\n<geshi lang=\"lua\">\n    ngx.location.capture('/foo?a=1&b=3&c=%3a')\n</geshi>\n\nthat is, this method will escape argument keys and values according to URI rules and\nconcatenate them together into a complete query string. The format for the Lua table passed as the <code>args</code> argument is identical to the format used in the [[#ngx.encode_args|ngx.encode_args]] method.\n\nThe <code>args</code> option can also take plain query strings:\n\n<geshi lang=\"lua\">\n    ngx.location.capture('/foo?a=1',\n        { args = 'b=3&c=%3a' }\n    )\n</geshi>\n\nThis is functionally identical to the previous examples.\n\nThe <code>share_all_vars</code> option controls whether to share Nginx variables among the current request and its subrequests.\nIf this option is set to <code>true</code>, then the current request and associated subrequests will share the same Nginx variable scope. Hence, changes to Nginx variables made by a subrequest will affect the current request.\n\nCare should be taken in using this option as variable scope sharing can have unexpected side effects. The <code>args</code>, <code>vars</code>, or <code>copy_all_vars</code> options are generally preferable instead.\n\nThis option is set to <code>false</code> by default\n\n<geshi lang=\"nginx\">\n    location /other {\n        set $dog \"$dog world\";\n        echo \"$uri dog: $dog\";\n    }\n\n    location /lua {\n        set $dog 'hello';\n        content_by_lua_block {\n            res = ngx.location.capture(\"/other\",\n                { share_all_vars = true })\n\n            ngx.print(res.body)\n            ngx.say(ngx.var.uri, \": \", ngx.var.dog)\n        }\n    }\n</geshi>\n\nAccessing location <code>/lua</code> gives\n\n<geshi lang=\"text\">\n    /other dog: hello world\n    /lua: hello world\n</geshi>\n\nThe <code>copy_all_vars</code> option provides a copy of the parent request's Nginx variables to subrequests when such subrequests are issued. Changes made to these variables by such subrequests will not affect the parent request or any other subrequests sharing the parent request's variables.\n\n<geshi lang=\"nginx\">\n    location /other {\n        set $dog \"$dog world\";\n        echo \"$uri dog: $dog\";\n    }\n\n    location /lua {\n        set $dog 'hello';\n        content_by_lua_block {\n            res = ngx.location.capture(\"/other\",\n                { copy_all_vars = true })\n\n            ngx.print(res.body)\n            ngx.say(ngx.var.uri, \": \", ngx.var.dog)\n        }\n    }\n</geshi>\n\nRequest <code>GET /lua</code> will give the output\n\n<geshi lang=\"text\">\n    /other dog: hello world\n    /lua: hello\n</geshi>\n\nNote that if both <code>share_all_vars</code> and <code>copy_all_vars</code> are set to true, then <code>share_all_vars</code> takes precedence.\n\nIn addition to the two settings above, it is possible to specify\nvalues for variables in the subrequest using the <code>vars</code> option. These\nvariables are set after the sharing or copying of variables has been\nevaluated, and provides a more efficient method of passing specific\nvalues to a subrequest over encoding them as URL arguments and\nunescaping them in the Nginx config file.\n\n<geshi lang=\"nginx\">\n    location /other {\n        content_by_lua_block {\n            ngx.say(\"dog = \", ngx.var.dog)\n            ngx.say(\"cat = \", ngx.var.cat)\n        }\n    }\n\n    location /lua {\n        set $dog '';\n        set $cat '';\n        content_by_lua_block {\n            res = ngx.location.capture(\"/other\",\n                { vars = { dog = \"hello\", cat = 32 }})\n\n            ngx.print(res.body)\n        }\n    }\n</geshi>\n\nAccessing <code>/lua</code> will yield the output\n\n<geshi lang=\"text\">\n    dog = hello\n    cat = 32\n</geshi>\n\nThe <code>ctx</code> option can be used to specify a custom Lua table to serve as the [[#ngx.ctx|ngx.ctx]] table for the subrequest.\n\n<geshi lang=\"nginx\">\n    location /sub {\n        content_by_lua_block {\n            ngx.ctx.foo = \"bar\";\n        }\n    }\n    location /lua {\n        content_by_lua_block {\n            local ctx = {}\n            res = ngx.location.capture(\"/sub\", { ctx = ctx })\n\n            ngx.say(ctx.foo)\n            ngx.say(ngx.ctx.foo)\n        }\n    }\n</geshi>\n\nThen request <code>GET /lua</code> gives\n\n<geshi lang=\"text\">\n    bar\n    nil\n</geshi>\n\nIt is also possible to use this <code>ctx</code> option to share the same [[#ngx.ctx|ngx.ctx]] table between the current (parent) request and the subrequest:\n\n<geshi lang=\"nginx\">\n    location /sub {\n        content_by_lua_block {\n            ngx.ctx.foo = \"bar\"\n        }\n    }\n    location /lua {\n        content_by_lua_block {\n            res = ngx.location.capture(\"/sub\", { ctx = ngx.ctx })\n            ngx.say(ngx.ctx.foo)\n        }\n    }\n</geshi>\n\nRequest <code>GET /lua</code> yields the output\n\n<geshi lang=\"text\">\n    bar\n</geshi>\n\nNote that subrequests issued by [[#ngx.location.capture|ngx.location.capture]] inherit all the\nrequest headers of the current request by default and that this may have unexpected side effects on the\nsubrequest responses. For example, when using the standard <code>ngx_proxy</code> module to serve\nsubrequests, an \"Accept-Encoding: gzip\" header in the main request may result\nin gzipped responses that cannot be handled properly in Lua code. Original request headers should be ignored by setting\n[[HttpProxyModule#proxy_pass_request_headers|proxy_pass_request_headers]] to <code>off</code> in subrequest locations.\n\nWhen the <code>body</code> option is not specified and the <code>always_forward_body</code> option is false (the default value), the <code>POST</code> and <code>PUT</code> subrequests will inherit the request bodies of the parent request (if any).\n\nThere is a hard-coded upper limit on the number of subrequests possible for every main request. In older versions of Nginx, the limit was <code>50</code> concurrent subrequests and in more recent versions, Nginx <code>1.9.5</code> onwards, the same limit is changed to limit the depth of recursive subrequests. When this limit is exceeded, the following error message is added to the <code>error.log</code> file:\n\n<geshi lang=\"text\">\n    [error] 13983#0: *1 subrequests cycle while processing \"/uri\"\n</geshi>\n\nThe limit can be manually modified if required by editing the definition of the <code>NGX_HTTP_MAX_SUBREQUESTS</code> macro in the <code>nginx/src/http/ngx_http_request.h</code> file in the Nginx source tree.\n\nPlease also refer to restrictions on capturing locations configured by [[#Locations_Configured_by_Subrequest_Directives_of_Other_Modules|subrequest directives of other modules]].\n\n== ngx.location.capture_multi ==\n\n'''syntax:''' ''res1, res2, ... = ngx.location.capture_multi({ {uri, options?}, {uri, options?}, ... })''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nJust like [[#ngx.location.capture|ngx.location.capture]], but supports multiple subrequests running in parallel.\n\nThis function issues several parallel subrequests specified by the input table and returns their results in the same order. For example,\n\n<geshi lang=\"lua\">\n    res1, res2, res3 = ngx.location.capture_multi{\n        { \"/foo\", { args = \"a=3&b=4\" } },\n        { \"/bar\" },\n        { \"/baz\", { method = ngx.HTTP_POST, body = \"hello\" } },\n    }\n\n    if res1.status == ngx.HTTP_OK then\n        ...\n    end\n\n    if res2.body == \"BLAH\" then\n        ...\n    end\n</geshi>\n\nThis function will not return until all the subrequests terminate.\nThe total latency is the longest latency of the individual subrequests rather than the sum.\n\nLua tables can be used for both requests and responses when the number of subrequests to be issued is not known in advance:\n\n<geshi lang=\"lua\">\n    -- construct the requests table\n    local reqs = {}\n    table.insert(reqs, { \"/mysql\" })\n    table.insert(reqs, { \"/postgres\" })\n    table.insert(reqs, { \"/redis\" })\n    table.insert(reqs, { \"/memcached\" })\n\n    -- issue all the requests at once and wait until they all return\n    local resps = {\n        ngx.location.capture_multi(reqs)\n    }\n\n    -- loop over the responses table\n    for i, resp in ipairs(resps) do\n        -- process the response table \"resp\"\n    end\n</geshi>\n\nThe [[#ngx.location.capture|ngx.location.capture]] function is just a special form\nof this function. Logically speaking, the [[#ngx.location.capture|ngx.location.capture]] can be implemented like this\n\n<geshi lang=\"lua\">\n    ngx.location.capture =\n        function (uri, args)\n            return ngx.location.capture_multi({ {uri, args} })\n        end\n</geshi>\n\nPlease also refer to restrictions on capturing locations configured by [[#Locations_Configured_by_Subrequest_Directives_of_Other_Modules|subrequest directives of other modules]].\n\n== ngx.status ==\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*''\n\nRead and write the current request's response status. This should be called\nbefore sending out the response headers.\n\n<geshi lang=\"lua\">\n    ngx.status = ngx.HTTP_CREATED\n    status = ngx.status\n</geshi>\n\nSetting <code>ngx.status</code> after the response header is sent out has no effect but leaving an error message in your Nginx's error log file:\n\n<geshi lang=\"text\">\n    attempt to set ngx.status after sending out response headers\n</geshi>\n\n== ngx.header.HEADER ==\n\n'''syntax:''' ''ngx.header.HEADER = VALUE''\n\n'''syntax:''' ''value = ngx.header.HEADER''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*''\n\nSet, add to, or clear the current request's <code>HEADER</code> response header that is to be sent.\n\nUnderscores (<code>_</code>) in the header names will be replaced by hyphens (<code>-</code>) by default. This transformation can be turned off via the [[#lua_transform_underscores_in_response_headers|lua_transform_underscores_in_response_headers]] directive.\n\nThe header names are matched case-insensitively.\n\n<geshi lang=\"lua\">\n    -- equivalent to ngx.header[\"Content-Type\"] = 'text/plain'\n    ngx.header.content_type = 'text/plain'\n\n    ngx.header[\"X-My-Header\"] = 'blah blah'\n</geshi>\n\nMulti-value headers can be set this way:\n\n<geshi lang=\"lua\">\n    ngx.header['Set-Cookie'] = {'a=32; path=/', 'b=4; path=/'}\n</geshi>\n\nwill yield\n\n<geshi lang=\"bash\">\n    Set-Cookie: a=32; path=/\n    Set-Cookie: b=4; path=/\n</geshi>\n\nin the response headers.\n\nOnly Lua tables are accepted (Only the last element in the table will take effect for standard headers such as <code>Content-Type</code> that only accept a single value).\n\n<geshi lang=\"lua\">\n    ngx.header.content_type = {'a', 'b'}\n</geshi>\n\nis equivalent to\n\n<geshi lang=\"lua\">\n    ngx.header.content_type = 'b'\n</geshi>\n\nSetting a slot to <code>nil</code> effectively removes it from the response headers:\n\n<geshi lang=\"lua\">\n    ngx.header[\"X-My-Header\"] = nil\n</geshi>\n\nThe same applies to assigning an empty table:\n\n<geshi lang=\"lua\">\n    ngx.header[\"X-My-Header\"] = {}\n</geshi>\n\nSetting <code>ngx.header.HEADER</code> after sending out response headers (either explicitly with [[#ngx.send_headers|ngx.send_headers]] or implicitly with [[#ngx.print|ngx.print]] and similar) will log an error message.\n\nReading <code>ngx.header.HEADER</code> will return the value of the response header named <code>HEADER</code>.\n\nUnderscores (<code>_</code>) in the header names will also be replaced by dashes (<code>-</code>) and the header names will be matched case-insensitively. If the response header is not present at all, <code>nil</code> will be returned.\n\nThis is particularly useful in the context of [[#header_filter_by_lua|header_filter_by_lua*]], for example,\n\n<geshi lang=\"nginx\">\n    location /test {\n        set $footer '';\n\n        proxy_pass http://some-backend;\n\n        header_filter_by_lua_block {\n            if ngx.header[\"X-My-Header\"] == \"blah\" then\n                ngx.var.footer = \"some value\"\n            end\n        }\n\n        echo_after_body $footer;\n    }\n</geshi>\n\nFor multi-value headers, all of the values of header will be collected in order and returned as a Lua table. For example, response headers\n\n<geshi lang=\"text\">\n    Foo: bar\n    Foo: baz\n</geshi>\n\nwill result in\n\n<geshi lang=\"lua\">\n    {\"bar\", \"baz\"}\n</geshi>\n\nto be returned when reading <code>ngx.header.Foo</code>.\n\nNote that <code>ngx.header</code> is not a normal Lua table and as such, it is not possible to iterate through it using the Lua <code>ipairs</code> function.\n\nNote: this function throws a Lua error if <code>HEADER</code> or\n<code>VALUE</code> contain unsafe characters (control characters).\n\nFor reading ''request'' headers, use the [[#ngx.req.get_headers|ngx.req.get_headers]] function instead.\n\n== ngx.resp.get_headers ==\n\n'''syntax:''' ''headers, err = ngx.resp.get_headers(max_headers?, raw?)''\n\n'''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*''\n\nReturns a Lua table holding all the current response headers for the current request.\n\n<geshi lang=\"lua\">\nlocal h, err = ngx.resp.get_headers()\n\nif err == \"truncated\" then\n    -- one can choose to ignore or reject the current response here\nend\n\nfor k, v in pairs(h) do\n    ...\nend\n</geshi>\n\nThis function has the same signature as [[#ngx.req.get_headers|ngx.req.get_headers]] except getting response headers instead of request headers.\n\nNote that a maximum of 100 response headers are parsed by default (including those with the same name) and that additional response headers are silently discarded to guard against potential denial of service attacks. Since <code>v0.10.13</code>, when the limit is exceeded, it will return a second value which is the string `\"truncated\"`.\n\nThis API was first introduced in the <code>v0.9.5</code> release.\n\n== ngx.req.is_internal ==\n\n'''syntax:''' ''is_internal = ngx.req.is_internal()''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*''\n\nReturns a boolean indicating whether the current request is an \"internal request\", i.e.,\na request initiated from inside the current Nginx server instead of from the client side.\n\nSubrequests are all internal requests and so are requests after internal redirects.\n\nThis API was first introduced in the <code>v0.9.20</code> release.\n\n== ngx.req.start_time ==\n\n'''syntax:''' ''secs = ngx.req.start_time()''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*''\n\nReturns a floating-point number representing the timestamp (including milliseconds as the decimal part) when the current request was created.\n\nThe following example emulates the <code>$request_time</code> variable value (provided by [[HttpLogModule]]) in pure Lua:\n\n<geshi lang=\"lua\">\n    local request_time = ngx.now() - ngx.req.start_time()\n</geshi>\n\nThis function was first introduced in the <code>v0.7.7</code> release.\n\nSee also [[#ngx.now|ngx.now]] and [[#ngx.update_time|ngx.update_time]].\n\n== ngx.req.http_version ==\n\n'''syntax:''' ''num = ngx.req.http_version()''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*''\n\nReturns the HTTP version number for the current request as a Lua number.\n\nCurrent possible values are 2.0, 1.0, 1.1, and 0.9. Returns <code>nil</code> for unrecognized values.\n\nThis method was first introduced in the <code>v0.7.17</code> release.\n\n== ngx.req.raw_header ==\n\n'''syntax:''' ''str = ngx.req.raw_header(no_request_line?)''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*''\n\nReturns the original raw HTTP protocol header received by the Nginx server.\n\nBy default, the request line and trailing <code>CR LF</code> terminator will also be included. For example,\n\n<geshi lang=\"lua\">\n    ngx.print(ngx.req.raw_header())\n</geshi>\n\ngives something like this:\n\n<geshi lang=\"text\">\n    GET /t HTTP/1.1\n    Host: localhost\n    Connection: close\n    Foo: bar\n\n</geshi>\n\nYou can specify the optional\n<code>no_request_line</code> argument as a <code>true</code> value to exclude the request line from the result. For example,\n\n<geshi lang=\"lua\">\n    ngx.print(ngx.req.raw_header(true))\n</geshi>\n\noutputs something like this:\n\n<geshi lang=\"text\">\n    Host: localhost\n    Connection: close\n    Foo: bar\n\n</geshi>\n\nThis method was first introduced in the <code>v0.7.17</code> release.\n\nThis method does not work in HTTP/2 requests yet.\n\n== ngx.req.get_method ==\n\n'''syntax:''' ''method_name = ngx.req.get_method()''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, balancer_by_lua*, log_by_lua*''\n\nRetrieves the current request's request method name. Strings like <code>\"GET\"</code> and <code>\"POST\"</code> are returned instead of numerical [[#HTTP method constants|method constants]].\n\nIf the current request is an Nginx subrequest, then the subrequest's method name will be returned.\n\nThis method was first introduced in the <code>v0.5.6</code> release.\n\nSee also [[#ngx.req.set_method|ngx.req.set_method]].\n\n== ngx.req.set_method ==\n\n'''syntax:''' ''ngx.req.set_method(method_id)''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*''\n\nOverrides the current request's request method with the <code>method_id</code> argument. Currently only numerical [[#HTTP method constants|method constants]] are supported, like <code>ngx.HTTP_POST</code> and <code>ngx.HTTP_GET</code>.\n\nIf the current request is an Nginx subrequest, then the subrequest's method will be overridden.\n\nThis method was first introduced in the <code>v0.5.6</code> release.\n\nSee also [[#ngx.req.get_method|ngx.req.get_method]].\n\n== ngx.req.set_uri ==\n\n'''syntax:''' ''ngx.req.set_uri(uri, jump?, binary?)''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*''\n\nRewrite the current request's (parsed) URI by the <code>uri</code> argument. The <code>uri</code> argument must be a Lua string and cannot be of zero length, or a Lua exception will be thrown.\n\nThe optional boolean <code>jump</code> argument can trigger location rematch (or location jump) as [[HttpRewriteModule]]'s [[HttpRewriteModule#rewrite|rewrite]] directive, that is, when <code>jump</code> is <code>true</code> (default to <code>false</code>), this function will never return and it will tell Nginx to try re-searching locations with the new URI value at the later <code>post-rewrite</code> phase and jumping to the new location.\n\nLocation jump will not be triggered otherwise, and only the current request's URI will be modified, which is also the default behavior. This function will return but with no returned values when the <code>jump</code> argument is <code>false</code> or absent altogether.\n\nFor example, the following Nginx config snippet\n\n<geshi lang=\"nginx\">\n    rewrite ^ /foo last;\n</geshi>\n\ncan be coded in Lua like this:\n\n<geshi lang=\"lua\">\n    ngx.req.set_uri(\"/foo\", true)\n</geshi>\n\nSimilarly, Nginx config\n\n<geshi lang=\"nginx\">\n    rewrite ^ /foo break;\n</geshi>\n\ncan be coded in Lua as\n\n<geshi lang=\"lua\">\n    ngx.req.set_uri(\"/foo\", false)\n</geshi>\n\nor equivalently,\n\n<geshi lang=\"lua\">\n    ngx.req.set_uri(\"/foo\")\n</geshi>\n\nThe <code>jump</code> argument can only be set to <code>true</code> in [[#rewrite_by_lua|rewrite_by_lua*]]. Use of jump in other contexts is prohibited and will throw out a Lua exception.\n\nA more sophisticated example involving regex substitutions is as follows\n\n<geshi lang=\"nginx\">\n    location /test {\n        rewrite_by_lua_block {\n            local uri = ngx.re.sub(ngx.var.uri, \"^/test/(.*)\", \"/$1\", \"o\")\n            ngx.req.set_uri(uri)\n        }\n        proxy_pass http://my_backend;\n    }\n</geshi>\n\nwhich is functionally equivalent to\n\n<geshi lang=\"nginx\">\n    location /test {\n        rewrite ^/test/(.*) /$1 break;\n        proxy_pass http://my_backend;\n    }\n</geshi>\n\nNote: this function throws a Lua error if the <code>uri</code> argument\ncontains unsafe characters (control characters).\n\nNote that it is not possible to use this interface to rewrite URI arguments and that [[#ngx.req.set_uri_args|ngx.req.set_uri_args]] should be used for this instead. For instance, Nginx config\n\n<geshi lang=\"nginx\">\n    rewrite ^ /foo?a=3? last;\n</geshi>\n\ncan be coded as\n\n<geshi lang=\"nginx\">\n    ngx.req.set_uri_args(\"a=3\")\n    ngx.req.set_uri(\"/foo\", true)\n</geshi>\n\nor\n\n<geshi lang=\"nginx\">\n    ngx.req.set_uri_args({a = 3})\n    ngx.req.set_uri(\"/foo\", true)\n</geshi>\n\nStarting from <code>0.10.16</code> of this module, this function accepts an\noptional boolean <code>binary</code> argument to allow arbitrary binary URI\ndata. By default, this <code>binary</code> argument is false and this function\nwill throw out a Lua error such as the one below when the <code>uri</code>\nargument contains any control characters (ASCII Code 0 ~ 0x08, 0x0A ~ 0x1F and 0x7F).\n\n<geshi lang=\"text\">\n    [error] 23430#23430: *1 lua entry thread aborted: runtime error:\n    content_by_lua(nginx.conf:44):3: ngx.req.set_uri unsafe byte \"0x00\"\n    in \"\\x00foo\" (maybe you want to set the 'binary' argument?)\n</geshi>\n\nThis interface was first introduced in the <code>v0.3.1rc14</code> release.\n\n== ngx.req.set_uri_args ==\n\n'''syntax:''' ''ngx.req.set_uri_args(args)''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*''\n\nRewrite the current request's URI query arguments by the <code>args</code> argument. The <code>args</code> argument can be either a Lua string, as in\n\n<geshi lang=\"lua\">\n    ngx.req.set_uri_args(\"a=3&b=hello%20world\")\n</geshi>\n\nor a Lua table holding the query arguments' key-value pairs, as in\n\n<geshi lang=\"lua\">\n    ngx.req.set_uri_args({ a = 3, b = \"hello world\" })\n</geshi>\n\nIn the former case, i.e., when the whole query-string is provided directly,\nthe input Lua string should already be well-formed with the URI encoding.\nFor security considerations, this method will automatically escape any control and\nwhitespace characters (ASCII code 0x00 ~ 0x20 and 0x7F) in the Lua string.\n\nIn the latter case, this method will escape argument keys and values according to the URI escaping rule.\n\nMulti-value arguments are also supported:\n\n<geshi lang=\"lua\">\n    ngx.req.set_uri_args({ a = 3, b = {5, 6} })\n</geshi>\n\nwhich will result in a query string like <code>a=3&b=5&b=6</code>.\n\nThis interface was first introduced in the <code>v0.3.1rc13</code> release.\n\nSee also [[#ngx.req.set_uri|ngx.req.set_uri]].\n\n== ngx.req.get_uri_args ==\n\n'''syntax:''' ''args, err = ngx.req.get_uri_args(max_args?, tab?)''\n\n'''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*''\n\nReturns a Lua table holding all the current request URL query arguments. An optional <code>tab</code> argument\ncan be used to reuse the table returned by this method.\n\n<geshi lang=\"nginx\">\n    location = /test {\n        content_by_lua_block {\n            local args, err = ngx.req.get_uri_args()\n\n            if err == \"truncated\" then\n                -- one can choose to ignore or reject the current request here\n            end\n\n            for key, val in pairs(args) do\n                if type(val) == \"table\" then\n                    ngx.say(key, \": \", table.concat(val, \", \"))\n                else\n                    ngx.say(key, \": \", val)\n                end\n            end\n        }\n    }\n</geshi>\n\nThen <code>GET /test?foo=bar&bar=baz&bar=blah</code> will yield the response body\n\n<geshi lang=\"bash\">\n    foo: bar\n    bar: baz, blah\n</geshi>\n\nMultiple occurrences of an argument key will result in a table value holding all the values for that key in order.\n\nKeys and values are unescaped according to URI escaping rules. In the settings above, <code>GET /test?a%20b=1%61+2</code> will yield:\n\n<geshi lang=\"bash\">\n    a b: 1a 2\n</geshi>\n\nArguments without the <code>=<value></code> parts are treated as boolean arguments. <code>GET /test?foo&bar</code> will yield:\n\n<geshi lang=\"bash\">\n    foo: true\n    bar: true\n</geshi>\n\nThat is, they will take Lua boolean values <code>true</code>. However, they are different from arguments taking empty string values. <code>GET /test?foo=&bar=</code> will give something like\n\n<geshi lang=\"bash\">\n    foo:\n    bar:\n</geshi>\n\nEmpty key arguments are discarded. <code>GET /test?=hello&=world</code> will yield an empty output for instance.\n\nUpdating query arguments via the Nginx variable <code>$args</code> (or <code>ngx.var.args</code> in Lua) at runtime is also supported:\n\n<geshi lang=\"lua\">\n    ngx.var.args = \"a=3&b=42\"\n    local args, err = ngx.req.get_uri_args()\n</geshi>\n\nHere the <code>args</code> table will always look like\n\n<geshi lang=\"lua\">\n    {a = 3, b = 42}\n</geshi>\n\nregardless of the actual request query string.\n\nNote that a maximum of 100 request arguments are parsed by default (including those with the same name) and that additional request arguments are silently discarded to guard against potential denial of service attacks. Since <code>v0.10.13</code>, when the limit is exceeded, it will return a second value which is the string `\"truncated\"`.\n\nHowever, the optional <code>max_args</code> function argument can be used to override this limit:\n\n<geshi lang=\"lua\">\n    local args, err = ngx.req.get_uri_args(10)\n    if err == \"truncated\" then\n        -- one can choose to ignore or reject the current request here\n    end\n</geshi>\n\nThis argument can be set to zero to remove the limit and to process all request arguments received:\n\n<geshi lang=\"lua\">\n    local args, err = ngx.req.get_uri_args(0)\n</geshi>\n\nRemoving the <code>max_args</code> cap is strongly discouraged.\n\n== ngx.req.get_post_args ==\n\n'''syntax:''' ''args, err = ngx.req.get_post_args(max_args?)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*''\n\nReturns a Lua table holding all the current request POST query arguments (of the MIME type <code>application/x-www-form-urlencoded</code>). Call [[#ngx.req.read_body|ngx.req.read_body]] to read the request body first or turn on the [[#lua_need_request_body|lua_need_request_body]] directive to avoid errors.\n\n<geshi lang=\"nginx\">\n    location = /test {\n        content_by_lua_block {\n            ngx.req.read_body()\n            local args, err = ngx.req.get_post_args()\n\n            if err == \"truncated\" then\n                -- one can choose to ignore or reject the current request here\n            end\n\n            if not args then\n                ngx.say(\"failed to get post args: \", err)\n                return\n            end\n            for key, val in pairs(args) do\n                if type(val) == \"table\" then\n                    ngx.say(key, \": \", table.concat(val, \", \"))\n                else\n                    ngx.say(key, \": \", val)\n                end\n            end\n        }\n    }\n</geshi>\n\nThen\n\n<geshi lang=\"bash\">\n    # Post request with the body 'foo=bar&bar=baz&bar=blah'\n    $ curl --data 'foo=bar&bar=baz&bar=blah' localhost/test\n</geshi>\n\nwill yield the response body like\n\n<geshi lang=\"bash\">\n    foo: bar\n    bar: baz, blah\n</geshi>\n\nMultiple occurrences of an argument key will result in a table value holding all of the values for that key in order.\n\nKeys and values will be unescaped according to URI escaping rules.\n\nWith the settings above,\n\n<geshi lang=\"bash\">\n    # POST request with body 'a%20b=1%61+2'\n    $ curl -d 'a%20b=1%61+2' localhost/test\n</geshi>\n\nwill yield:\n\n<geshi lang=\"bash\">\n    a b: 1a 2\n</geshi>\n\nArguments without the <code>=<value></code> parts are treated as boolean arguments. <code>POST /test</code> with the request body <code>foo&bar</code> will yield:\n\n<geshi lang=\"bash\">\n    foo: true\n    bar: true\n</geshi>\n\nThat is, they will take Lua boolean values <code>true</code>. However, they are different from arguments taking empty string values. <code>POST /test</code> with request body <code>foo=&bar=</code> will return something like\n\n<geshi lang=\"bash\">\n    foo:\n    bar:\n</geshi>\n\nEmpty key arguments are discarded. <code>POST /test</code> with body <code>=hello&=world</code> will yield empty outputs for instance.\n\nNote that a maximum of 100 request arguments are parsed by default (including those with the same name) and that additional request arguments are silently discarded to guard against potential denial of service attacks. Since <code>v0.10.13</code>, when the limit is exceeded, it will return a second value which is the string `\"truncated\"`.\n\nHowever, the optional <code>max_args</code> function argument can be used to override this limit:\n\n<geshi lang=\"lua\">\n    local args, err = ngx.req.get_post_args(10)\n    if err == \"truncated\" then\n        -- one can choose to ignore or reject the current request here\n    end\n</geshi>\n\nThis argument can be set to zero to remove the limit and to process all request arguments received:\n\n<geshi lang=\"lua\">\n    local args, err = ngx.req.get_post_args(0)\n</geshi>\n\nRemoving the <code>max_args</code> cap is strongly discouraged.\n\n== ngx.req.get_headers ==\n\n'''syntax:''' ''headers, err = ngx.req.get_headers(max_headers?, raw?)''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*''\n\nReturns a Lua table holding all the current request headers.\n\n<geshi lang=\"lua\">\n    local h, err = ngx.req.get_headers()\n\n    if err == \"truncated\" then\n        -- one can choose to ignore or reject the current request here\n    end\n\n    for k, v in pairs(h) do\n        ...\n    end\n</geshi>\n\nTo read an individual header:\n\n<geshi lang=\"lua\">\n    ngx.say(\"Host: \", ngx.req.get_headers()[\"Host\"])\n</geshi>\n\nNote that the [[#ngx.var.VARIABLE|ngx.var.HEADER]] API call, which uses core [[HttpCoreModule#$http_HEADER|$http_HEADER]] variables, may be more preferable for reading individual request headers.\n\nFor multiple instances of request headers such as:\n\n<geshi lang=\"bash\">\n    Foo: foo\n    Foo: bar\n    Foo: baz\n</geshi>\n\nthe value of <code>ngx.req.get_headers()[\"Foo\"]</code> will be a Lua (array) table such as:\n\n<geshi lang=\"lua\">\n    {\"foo\", \"bar\", \"baz\"}\n</geshi>\n\nNote that a maximum of 100 request headers are parsed by default (including those with the same name) and that additional request headers are silently discarded to guard against potential denial of service attacks. Since <code>v0.10.13</code>, when the limit is exceeded, it will return a second value which is the string `\"truncated\"`.\n\nHowever, the optional <code>max_headers</code> function argument can be used to override this limit:\n\n<geshi lang=\"lua\">\n    local headers, err = ngx.req.get_headers(10)\n\n    if err == \"truncated\" then\n        -- one can choose to ignore or reject the current request here\n    end\n</geshi>\n\nThis argument can be set to zero to remove the limit and to process all request headers received:\n\n<geshi lang=\"lua\">\n    local headers, err = ngx.req.get_headers(0)\n</geshi>\n\nRemoving the <code>max_headers</code> cap is strongly discouraged.\n\nSince the <code>0.6.9</code> release, all the header names in the Lua table returned are converted to the pure lower-case form by default, unless the <code>raw</code> argument is set to <code>true</code> (default to <code>false</code>).\n\nAlso, by default, an <code>__index</code> metamethod is added to the resulting Lua table and will normalize the keys to a pure lowercase form with all underscores converted to dashes in case of a lookup miss. For example, if a request header <code>My-Foo-Header</code> is present, then the following invocations will all pick up the value of this header correctly:\n\n<geshi lang=\"lua\">\n    ngx.say(headers.my_foo_header)\n    ngx.say(headers[\"My-Foo-Header\"])\n    ngx.say(headers[\"my-foo-header\"])\n</geshi>\n\nThe <code>__index</code> metamethod will not be added when the <code>raw</code> argument is set to <code>true</code>.\n\n== ngx.req.set_header ==\n\n'''syntax:''' ''ngx.req.set_header(header_name, header_value)''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*''\n\nSet the current request's request header named <code>header_name</code> to value <code>header_value</code>, overriding any existing ones.\n\nThe input Lua string `header_name` and `header_value` should already be well-formed with the URI encoding.\nFor security considerations, this method will automatically escape \" \", \"\"\", \"(\", \")\", \",\", \"/\", \":\", \";\", \"?\",\n\"<\", \"=\", \">\", \"?\", \"@\", \"[\", \"]\", \"\\\", \"{\", \"}\", 0x00-0x1F, 0x7F-0xFF in `header_name` and automatically escape\n\"0x00-0x08, 0x0A-0x0F, 0x7F in `header_value`.\n\nBy 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.\n\nIt 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.\n\nHere is an example of setting the <code>Content-Type</code> header:\n\n<geshi lang=\"lua\">\n    ngx.req.set_header(\"Content-Type\", \"text/css\")\n</geshi>\n\nThe <code>header_value</code> can take an array list of values,\nfor example,\n\n<geshi lang=\"lua\">\n    ngx.req.set_header(\"Foo\", {\"a\", \"abc\"})\n</geshi>\n\nwill produce two new request headers:\n\n<geshi lang=\"bash\">\n    Foo: a\n    Foo: abc\n</geshi>\n\nand old <code>Foo</code> headers will be overridden if there is any.\n\nWhen the <code>header_value</code> argument is <code>nil</code>, the request header will be removed. So\n\n<geshi lang=\"lua\">\n    ngx.req.set_header(\"X-Foo\", nil)\n</geshi>\n\nis equivalent to\n\n<geshi lang=\"lua\">\n    ngx.req.clear_header(\"X-Foo\")\n</geshi>\n\nNote: this function throws a Lua error if <code>header_name</code> or\n<code>header_value</code> contain unsafe characters (control characters).\n\n== ngx.req.clear_header ==\n\n'''syntax:''' ''ngx.req.clear_header(header_name)''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*''\n\nClears the current request's request header named <code>header_name</code>. None of the current request's existing subrequests will be affected but subsequently initiated subrequests will inherit the change by default.\n\n== ngx.req.read_body ==\n\n'''syntax:''' ''ngx.req.read_body()''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nReads the client request body synchronously without blocking the Nginx event loop.\n\n<geshi lang=\"lua\">\n    ngx.req.read_body()\n    local args = ngx.req.get_post_args()\n</geshi>\n\nIf the request body is already read previously by turning on [[#lua_need_request_body|lua_need_request_body]] or by using other modules, then this function does not run and returns immediately.\n\nIf the request body has already been explicitly discarded, either by the [[#ngx.req.discard_body|ngx.req.discard_body]] function or other modules, this function does not run and returns immediately.\n\nIn case of errors, such as connection errors while reading the data, this method will throw out a Lua exception ''or'' terminate the current request with a 500 status code immediately.\n\nThe request body data read using this function can be retrieved later via [[#ngx.req.get_body_data|ngx.req.get_body_data]] or, alternatively, the temporary file name for the body data cached to disk using [[#ngx.req.get_body_file|ngx.req.get_body_file]]. This depends on\n\n# whether the current request body is already larger than the [[HttpCoreModule#client_body_buffer_size|client_body_buffer_size]],\n# and whether [[HttpCoreModule#client_body_in_file_only|client_body_in_file_only]] has been switched on.\n\nIn cases where current request may have a request body and the request body data is not required, The [[#ngx.req.discard_body|ngx.req.discard_body]] function must be used to explicitly discard the request body to avoid breaking things under HTTP 1.1 keepalive or HTTP 1.1 pipelining.\n\nThis function was first introduced in the <code>v0.3.1rc17</code> release.\n\n== ngx.req.discard_body ==\n\n'''syntax:''' ''ngx.req.discard_body()''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nExplicitly discard the request body, i.e., read the data on the connection and throw it away immediately (without using the request body by any means).\n\nThis function is an asynchronous call and returns immediately.\n\nIf the request body has already been read, this function does nothing and returns immediately.\n\nThis function was first introduced in the <code>v0.3.1rc17</code> release.\n\nSee also [[#ngx.req.read_body|ngx.req.read_body]].\n\n== ngx.req.get_body_data ==\n\n'''syntax:''' ''data = ngx.req.get_body_data(max_bytes?)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, log_by_lua*''\n\nRetrieves 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.\n\nThe optional <code>max_bytes</code> function argument can be used when you don't need the entire body.\n\nThis function returns <code>nil</code> if\n\n# the request body has not been read,\n# the request body has been read into disk temporary files,\n# or the request body has zero size.\n\nIf the request body has not been read yet, call [[#ngx.req.read_body|ngx.req.read_body]] first (or turn on [[#lua_need_request_body|lua_need_request_body]] to force this module to read the request body. This is not recommended however).\n\nIf the request body has been read into disk files, try calling the [[#ngx.req.get_body_file|ngx.req.get_body_file]] function instead.\n\nTo force in-memory request bodies, try setting [[HttpCoreModule#client_body_buffer_size|client_body_buffer_size]] to the same size value in [[HttpCoreModule#client_max_body_size|client_max_body_size]].\n\nNote that calling this function instead of using <code>ngx.var.request_body</code> or <code>ngx.var.echo_request_body</code> is more efficient because it can save one dynamic memory allocation and one data copy.\n\nThis function was first introduced in the <code>v0.3.1rc17</code> release.\n\nSee also [[#ngx.req.get_body_file|ngx.req.get_body_file]].\n\n== ngx.req.get_body_file ==\n\n'''syntax:''' ''file_name = ngx.req.get_body_file()''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nRetrieves the file name for the in-file request body data. Returns <code>nil</code> if the request body has not been read or has been read into memory.\n\nThe returned file is read only and is usually cleaned up by Nginx's memory pool. It should not be manually modified, renamed, or removed in Lua code.\n\nIf the request body has not been read yet, call [[#ngx.req.read_body|ngx.req.read_body]] first (or turn on [[#lua_need_request_body|lua_need_request_body]] to force this module to read the request body. This is not recommended however).\n\nIf the request body has been read into memory, try calling the [[#ngx.req.get_body_data|ngx.req.get_body_data]] function instead.\n\nTo force in-file request bodies, try turning on [[HttpCoreModule#client_body_in_file_only|client_body_in_file_only]].\n\nThis function was first introduced in the <code>v0.3.1rc17</code> release.\n\nSee also [[#ngx.req.get_body_data|ngx.req.get_body_data]].\n\n== ngx.req.set_body_data ==\n\n'''syntax:''' ''ngx.req.set_body_data(data)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, balancer_by_lua*''\n\nSet the current request's request body using the in-memory data specified by the <code>data</code> argument.\n\nIf the request body has not been read yet, call [[#ngx.req.read_body|ngx.req.read_body]] first (or turn on [[#lua_need_request_body|lua_need_request_body]] to force this module to read the request body. This is not recommended however). Additionally, the request body must not have been previously discarded by [[#ngx.req.discard_body|ngx.req.discard_body]].\n\nWhether 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.\n\nNote 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.\n\nThis function was first introduced in the <code>v0.3.1rc18</code> release.\n\nSee also [[#ngx.req.set_body_file|ngx.req.set_body_file]].\n\n== ngx.req.set_body_file ==\n\n'''syntax:''' ''ngx.req.set_body_file(file_name, auto_clean?)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, balancer_by_lua*''\n\nSet the current request's request body using the in-file data specified by the <code>file_name</code> argument.\n\nIf the request body has not been read yet, call [[#ngx.req.read_body|ngx.req.read_body]] first (or turn on [[#lua_need_request_body|lua_need_request_body]] to force this module to read the request body. This is not recommended however). Additionally, the request body must not have been previously discarded by [[#ngx.req.discard_body|ngx.req.discard_body]].\n\nIf the optional <code>auto_clean</code> argument is given a <code>true</code> value, then this file will be removed at request completion or the next time this function or [[#ngx.req.set_body_data|ngx.req.set_body_data]] are called in the same request. The <code>auto_clean</code> is default to <code>false</code>.\n\nPlease ensure that the file specified by the <code>file_name</code> argument exists and is readable by an Nginx worker process by setting its permission properly to avoid Lua exception errors.\n\nWhether 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.\n\nNote 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.\n\nThis function was first introduced in the <code>v0.3.1rc18</code> release.\n\nSee also [[#ngx.req.set_body_data|ngx.req.set_body_data]].\n\n== ngx.req.init_body ==\n\n'''syntax:''' ''ngx.req.init_body(buffer_size?)''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nCreates 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.\n\nIf the <code>buffer_size</code> 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.\n\nWhen the data can no longer be hold in the memory buffer for the request body, then the data will be flushed onto a temporary file just like the standard request body reader in the Nginx core.\n\nIt is important to always call the [[#ngx.req.finish_body|ngx.req.finish_body]] after all the data has been appended onto the current request body. Also, when this function is used together with [[#ngx.req.socket|ngx.req.socket]], it is required to call [[#ngx.req.socket|ngx.req.socket]] ''before'' this function, or you will get the \"request body already exists\" error message.\n\nThe usage of this function is often like this:\n\n<geshi lang=\"lua\">\n    ngx.req.init_body(128 * 1024)  -- buffer is 128KB\n    for chunk in next_data_chunk() do\n        ngx.req.append_body(chunk) -- each chunk can be 4KB\n    end\n    ngx.req.finish_body()\n</geshi>\n\nThis function can be used with [[#ngx.req.append_body|ngx.req.append_body]], [[#ngx.req.finish_body|ngx.req.finish_body]], and [[#ngx.req.socket|ngx.req.socket]] to implement efficient input filters in pure Lua (in the context of [[#rewrite_by_lua|rewrite_by_lua*]] or [[#access_by_lua|access_by_lua*]]), which can be used with other Nginx content handler or upstream modules like [[HttpProxyModule]] and [[HttpFastcgiModule]].\n\nThis function was first introduced in the <code>v0.5.11</code> release.\n\n== ngx.req.append_body ==\n\n'''syntax:''' ''ngx.req.append_body(data_chunk)''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nAppend new data chunk specified by the <code>data_chunk</code> argument onto the existing request body created by the [[#ngx.req.init_body|ngx.req.init_body]] call.\n\nWhen the data can no longer be hold in the memory buffer for the request body, then the data will be flushed onto a temporary file just like the standard request body reader in the Nginx core.\n\nIt is important to always call the [[#ngx.req.finish_body|ngx.req.finish_body]] after all the data has been appended onto the current request body.\n\nThis function can be used with [[#ngx.req.init_body|ngx.req.init_body]], [[#ngx.req.finish_body|ngx.req.finish_body]], and [[#ngx.req.socket|ngx.req.socket]] to implement efficient input filters in pure Lua (in the context of [[#rewrite_by_lua|rewrite_by_lua*]] or [[#access_by_lua|access_by_lua*]]), which can be used with other Nginx content handler or upstream modules like [[HttpProxyModule]] and [[HttpFastcgiModule]].\n\nThis function was first introduced in the <code>v0.5.11</code> release.\n\nSee also [[#ngx.req.init_body|ngx.req.init_body]].\n\n== ngx.req.finish_body ==\n\n'''syntax:''' ''ngx.req.finish_body()''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nCompletes the construction process of the new request body created by the [[#ngx.req.init_body|ngx.req.init_body]] and [[#ngx.req.append_body|ngx.req.append_body]] calls.\n\nThis function can be used with [[#ngx.req.init_body|ngx.req.init_body]], [[#ngx.req.append_body|ngx.req.append_body]], and [[#ngx.req.socket|ngx.req.socket]] to implement efficient input filters in pure Lua (in the context of [[#rewrite_by_lua|rewrite_by_lua*]] or [[#access_by_lua|access_by_lua*]]), which can be used with other Nginx content handler or upstream modules like [[HttpProxyModule]] and [[HttpFastcgiModule]].\n\nThis function was first introduced in the <code>v0.5.11</code> release.\n\nSee also [[#ngx.req.init_body|ngx.req.init_body]].\n\n== ngx.req.socket ==\n\n'''syntax:''' ''tcpsock, err = ngx.req.socket()''\n\n'''syntax:''' ''tcpsock, err = ngx.req.socket(raw)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nReturns a read-only cosocket object that wraps the downstream connection. Only [[#tcpsock:receive|receive]], [[#tcpsock:receiveany|receiveany]] and [[#tcpsock:receiveuntil|receiveuntil]] methods are supported on this object.\n\nIn case of error, <code>nil</code> will be returned as well as a string describing the error.\n\n'''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 <code>408 Request Time-out</code> or <code>413 Request Entity Too Large</code> response will be returned to the client instead.\nThe 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]].\n\nIf 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.\nChunked request bodies are not yet supported in this API.\n\nSince the <code>v0.9.0</code> release, this function accepts an optional boolean <code>raw</code> argument. When this argument is <code>true</code>, this function returns a full-duplex cosocket object wrapping around the raw downstream connection socket, upon which you can call the [[#tcpsock:receive|receive]], [[#tcpsock:receiveany|receiveany]], [[#tcpsock:receiveuntil|receiveuntil]], and [[#tcpsock:send|send]] methods.\n\nWhen the <code>raw</code> argument is <code>true</code>, it is required that no pending data from any previous [[#ngx.say|ngx.say]], [[#ngx.print|ngx.print]], or [[#ngx.send_headers|ngx.send_headers]] calls exists. So if you have these downstream output calls previously, you should call [[#ngx.flush|ngx.flush(true)]] before calling <code>ngx.req.socket(true)</code> to ensure that there is no pending output data. If the request body has not been read yet, then this \"raw socket\" can also be used to read the request body.\n\nYou can use the \"raw request socket\" returned by <code>ngx.req.socket(true)</code> to implement fancy protocols like [https://en.wikipedia.org/wiki/WebSocket WebSocket], or just emit your own raw HTTP response header or body data. You can refer to the [https://github.com/openresty/lua-resty-websocket lua-resty-websocket library] for a real world example.\n\nThis function was first introduced in the <code>v0.5.0rc1</code> release.\n\n== ngx.exec ==\n\n'''syntax:''' ''ngx.exec(uri, args?)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nDoes an internal redirect to <code>uri</code> with <code>args</code> and is similar to the [[HttpEchoModule#echo_exec|echo_exec]] directive of the [[HttpEchoModule]].\n\n<geshi lang=\"lua\">\n    ngx.exec('/some-location')\n    ngx.exec('/some-location', 'a=3&b=5&c=6')\n    ngx.exec('/some-location?a=3&b=5', 'c=6')\n</geshi>\n\nThe optional second <code>args</code> can be used to specify extra URI query arguments, for example:\n\n<geshi lang=\"lua\">\n    ngx.exec(\"/foo\", \"a=3&b=hello%20world\")\n</geshi>\n\nAlternatively, a Lua table can be passed for the <code>args</code> argument for ngx_lua to carry out URI escaping and string concatenation.\n\n<geshi lang=\"lua\">\n    ngx.exec(\"/foo\", { a = 3, b = \"hello world\" })\n</geshi>\n\nThe result is exactly the same as the previous example.\n\nThe format for the Lua table passed as the <code>args</code> argument is identical to the format used in the [[#ngx.encode_args|ngx.encode_args]] method.\n\nNamed locations are also supported but the second <code>args</code> argument will be ignored if present and the querystring for the new target is inherited from the referring location (if any).\n\n<code>GET /foo/file.php?a=hello</code> will return \"hello\" and not \"goodbye\" in the example below\n\n<geshi lang=\"nginx\">\n    location /foo {\n        content_by_lua_block {\n            ngx.exec(\"@bar\", \"a=goodbye\")\n        }\n    }\n\n    location @bar {\n        content_by_lua_block {\n            local args = ngx.req.get_uri_args()\n            for key, val in pairs(args) do\n                if key == \"a\" then\n                    ngx.say(val)\n                end\n            end\n        }\n    }\n</geshi>\n\nNote that the <code>ngx.exec</code> method is different from [[#ngx.redirect|ngx.redirect]] in that\nit is purely an internal redirect and that no new external HTTP traffic is involved.\n\nAlso note that this method call terminates the processing of the current request and that it ''must'' be called before [[#ngx.send_headers|ngx.send_headers]] or explicit response body\noutputs by either [[#ngx.print|ngx.print]] or [[#ngx.say|ngx.say]].\n\nIt is recommended that a coding style that combines this method call with the <code>return</code> statement, i.e., <code>return ngx.exec(...)</code> be adopted when this method call is used in contexts other than [[#header_filter_by_lua|header_filter_by_lua*]] to reinforce the fact that the request processing is being terminated.\n\n== ngx.redirect ==\n\n'''syntax:''' ''ngx.redirect(uri, status?)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nIssue an <code>HTTP 301</code> or <code>302</code> redirection to <code>uri</code>.\n\nNote: this function throws a Lua error if the <code>uri</code> argument\ncontains unsafe characters (control characters).\n\nThe optional <code>status</code> parameter specifies the HTTP status code to be used. The following status codes are supported right now:\n\n* <code>301</code>\n* <code>302</code> (default)\n* <code>303</code>\n* <code>307</code>\n* <code>308</code>\n\nIt is <code>302</code> (<code>ngx.HTTP_MOVED_TEMPORARILY</code>) by default.\n\nHere is an example assuming the current server name is <code>localhost</code> and that it is listening on port 1984:\n\n<geshi lang=\"lua\">\n    return ngx.redirect(\"/foo\")\n</geshi>\n\nwhich is equivalent to\n\n<geshi lang=\"lua\">\n    return ngx.redirect(\"/foo\", ngx.HTTP_MOVED_TEMPORARILY)\n</geshi>\n\nRedirecting arbitrary external URLs is also supported, for example:\n\n<geshi lang=\"lua\">\n    return ngx.redirect(\"http://www.google.com\")\n</geshi>\n\nWe can also use the numerical code directly as the second <code>status</code> argument:\n\n<geshi lang=\"lua\">\n    return ngx.redirect(\"/foo\", 301)\n</geshi>\n\nThis method is similar to the [[HttpRewriteModule#rewrite|rewrite]] directive with the <code>redirect</code> modifier in the standard\n[[HttpRewriteModule]], for example, this <code>nginx.conf</code> snippet\n\n<geshi lang=\"nginx\">\n    rewrite ^ /foo? redirect;  # nginx config\n</geshi>\n\nis equivalent to the following Lua code\n\n<geshi lang=\"lua\">\n    return ngx.redirect('/foo')  -- Lua code\n</geshi>\n\nwhile\n\n<geshi lang=\"nginx\">\n    rewrite ^ /foo? permanent;  # nginx config\n</geshi>\n\nis equivalent to\n\n<geshi lang=\"lua\">\n    return ngx.redirect('/foo', ngx.HTTP_MOVED_PERMANENTLY)  -- Lua code\n</geshi>\n\nURI arguments can be specified as well, for example:\n\n<geshi lang=\"lua\">\n    return ngx.redirect('/foo?a=3&b=4')\n</geshi>\n\nNote that this method call terminates the processing of the current request and that it ''must'' be called before [[#ngx.send_headers|ngx.send_headers]] or explicit response body\noutputs by either [[#ngx.print|ngx.print]] or [[#ngx.say|ngx.say]].\n\nIt is recommended that a coding style that combines this method call with the <code>return</code> statement, i.e., <code>return ngx.redirect(...)</code> be adopted when this method call is used in contexts other than [[#header_filter_by_lua|header_filter_by_lua*]] to reinforce the fact that the request processing is being terminated.\n\n== ngx.send_headers ==\n\n'''syntax:''' ''ok, err = ngx.send_headers()''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nExplicitly send out the response headers.\n\nSince <code>v0.8.3</code> this function returns <code>1</code> on success, or returns <code>nil</code> and a string describing the error otherwise.\n\nNote that there is normally no need to manually send out response headers as ngx_lua will automatically send headers out\nbefore content is output with [[#ngx.say|ngx.say]] or [[#ngx.print|ngx.print]] or when [[#content_by_lua|content_by_lua*]] exits normally.\n\n== ngx.headers_sent ==\n\n'''syntax:''' ''value = ngx.headers_sent''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nReturns <code>true</code> if the response headers have been sent (by ngx_lua), and <code>false</code> otherwise.\n\nThis API was first introduced in ngx_lua v0.3.1rc6.\n\n== ngx.print ==\n\n'''syntax:''' ''ok, err = ngx.print(...)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nEmits arguments concatenated to the HTTP client (as response body). If response headers have not been sent, this function will send headers out first and then output body data.\n\nSince <code>v0.8.3</code> this function returns <code>1</code> on success, or returns <code>nil</code> and a string describing the error otherwise.\n\nLua <code>nil</code> values will output <code>\"nil\"</code> strings and Lua boolean values will output <code>\"true\"</code> and <code>\"false\"</code> literal strings respectively.\n\nNested arrays of strings are permitted and the elements in the arrays will be sent one by one:\n\n<geshi lang=\"lua\">\n    local table = {\n        \"hello, \",\n        {\"world: \", true, \" or \", false,\n            {\": \", nil}}\n    }\n    ngx.print(table)\n</geshi>\n\nwill yield the output\n\n<geshi lang=\"bash\">\n    hello, world: true or false: nil\n</geshi>\n\nNon-array table arguments will cause a Lua exception to be thrown.\n\nThe <code>ngx.null</code> constant will yield the <code>\"null\"</code> string output.\n\nThis is an asynchronous call and will return immediately without waiting for all the data to be written into the system send buffer. To run in synchronous mode, call <code>ngx.flush(true)</code> after calling <code>ngx.print</code>. This can be particularly useful for streaming output. See [[#ngx.flush|ngx.flush]] for more details.\n\nPlease note that both <code>ngx.print</code> and [[#ngx.say|ngx.say]] will always invoke the whole Nginx output body filter chain, which is an expensive operation. So be careful when calling either of these two in a tight loop; buffer the data yourself in Lua and save the calls.\n\n== ngx.say ==\n\n'''syntax:''' ''ok, err = ngx.say(...)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nJust as [[#ngx.print|ngx.print]] but also emit a trailing newline.\n\n== ngx.log ==\n\n'''syntax:''' ''ngx.log(log_level, ...)''\n\n'''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*''\n\nLog arguments concatenated to error.log with the given logging level.\n\nLua <code>nil</code> arguments are accepted and result in literal <code>\"nil\"</code> string while Lua booleans result in literal <code>\"true\"</code> or <code>\"false\"</code> string outputs. And the <code>ngx.null</code> constant will yield the <code>\"null\"</code> string output.\n\nThe <code>log_level</code> argument can take constants like <code>ngx.ERR</code> and <code>ngx.WARN</code>. Check out [[#Nginx log level constants|Nginx log level constants]] for details.\n\nThere is a hard coded <code>2048</code> byte limitation on error message lengths in the Nginx core. This limit includes trailing newlines and leading time stamps. If the message size exceeds this limit, Nginx will truncate the message text accordingly. This limit can be manually modified by editing the <code>NGX_MAX_ERROR_STR</code> macro definition in the <code>src/core/ngx_log.h</code> file in the Nginx source tree.\n\n== ngx.flush ==\n\n'''syntax:''' ''ok, err = ngx.flush(wait?)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nFlushes response output to the client.\n\n<code>ngx.flush</code> accepts an optional boolean <code>wait</code> argument (Default: <code>false</code>) first introduced in the <code>v0.3.1rc34</code> release. When called with the default argument, it issues an asynchronous call (Returns immediately without waiting for output data to be written into the system send buffer). Calling the function with the <code>wait</code> argument set to <code>true</code> switches to synchronous mode.\n\nIn synchronous mode, the function will not return until all output data has been written into the system send buffer or until the [[HttpCoreModule#send_timeout|send_timeout]] setting has expired. Note that using the Lua coroutine mechanism means that this function does not block the Nginx event loop even in the synchronous mode.\n\nWhen <code>ngx.flush(true)</code> is called immediately after [[#ngx.print|ngx.print]] or [[#ngx.say|ngx.say]], it causes the latter functions to run in synchronous mode. This can be particularly useful for streaming output.\n\nNote that <code>ngx.flush</code> is not functional when in the HTTP 1.0 output buffering mode. See [[#HTTP 1.0 support|HTTP 1.0 support]].\n\nSince <code>v0.8.3</code> this function returns <code>1</code> on success, or returns <code>nil</code> and a string describing the error otherwise.\n\n== ngx.exit ==\n\n'''syntax:''' ''ngx.exit(status)''\n\n'''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*''\n\nWhen <code>status >= 200</code> (i.e., <code>ngx.HTTP_OK</code> and above), it will interrupt the execution of the current request and return status code to Nginx.\n\nWhen <code>status == 0</code> (i.e., <code>ngx.OK</code>), it will only quit the current phase handler (or the content handler if the [[#content_by_lua|content_by_lua*]] directive is used) and continue to run later phases (if any) for the current request.\n\nThe <code>status</code> argument can be <code>ngx.OK</code>, <code>ngx.ERROR</code>, <code>ngx.HTTP_NOT_FOUND</code>,\n<code>ngx.HTTP_MOVED_TEMPORARILY</code>, or other [[#HTTP status constants|HTTP status constants]].\n\nTo return an error page with custom contents, use code snippets like this:\n\n<geshi lang=\"lua\">\n    ngx.status = ngx.HTTP_GONE\n    ngx.say(\"This is our own content\")\n    -- to cause quit the whole request rather than the current phase handler\n    ngx.exit(ngx.HTTP_OK)\n</geshi>\n\nThe effect in action:\n\n<geshi lang=\"bash\">\n    $ curl -i http://localhost/test\n    HTTP/1.1 410 Gone\n    Server: nginx/1.0.6\n    Date: Thu, 15 Sep 2011 00:51:48 GMT\n    Content-Type: text/plain\n    Transfer-Encoding: chunked\n    Connection: keep-alive\n\n    This is our own content\n</geshi>\n\nNumber literals can be used directly as the argument, for instance,\n\n<geshi lang=\"lua\">\n    ngx.exit(501)\n</geshi>\n\nNote that while this method accepts all [[#HTTP status constants|HTTP status constants]] as input, it only accepts <code>ngx.OK</code> and <code>ngx.ERROR</code> of the [[#core constants|core constants]].\n\nAlso note that this method call terminates the processing of the current request and that it is recommended that a coding style that combines this method call with the <code>return</code> statement, i.e., <code>return ngx.exit(...)</code> be used to reinforce the fact that the request processing is being terminated.\n\nWhen being used in the contexts of [[#header_filter_by_lua|header_filter_by_lua*]], [[#balancer_by_lua_block|balancer_by_lua*]], and\n[[#ssl_session_store_by_lua_block|ssl_session_store_by_lua*]], <code>ngx.exit()</code> is\nan asynchronous operation and will return immediately. This behavior may change in future and it is recommended that users always use <code>return</code> in combination as suggested above.\n\n== ngx.eof ==\n\n'''syntax:''' ''ok, err = ngx.eof()''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nExplicitly specify the end of the response output stream. In the case of HTTP 1.1 chunked encoded output, it will just trigger the Nginx core to send out the \"last chunk\".\n\nWhen you disable the HTTP 1.1 keep-alive feature for your downstream connections, you can rely on well written HTTP clients to close the connection actively for you when you call this method. This trick can be used do back-ground jobs without letting the HTTP clients to wait on the connection, as in the following example:\n\n<geshi lang=\"nginx\">\n    location = /async {\n        keepalive_timeout 0;\n        content_by_lua_block {\n            ngx.say(\"got the task!\")\n            ngx.eof()  -- well written HTTP clients will close the connection at this point\n            -- access MySQL, PostgreSQL, Redis, Memcached, and etc here...\n        }\n    }\n</geshi>\n\nBut if you create subrequests to access other locations configured by Nginx upstream modules, then you should configure those upstream modules to ignore client connection abortions if they are not by default. For example, by default the standard [[HttpProxyModule]] will terminate both the subrequest and the main request as soon as the client closes the connection, so it is important to turn on the [[HttpProxyModule#proxy_ignore_client_abort|proxy_ignore_client_abort]] directive in your location block configured by [[HttpProxyModule]]:\n\n<geshi lang=\"nginx\">\n    proxy_ignore_client_abort on;\n</geshi>\n\nA better way to do background jobs is to use the [[#ngx.timer.at|ngx.timer.at]] API.\n\nSince <code>v0.8.3</code> this function returns <code>1</code> on success, or returns <code>nil</code> and a string describing the error otherwise.\n\n== ngx.sleep ==\n\n'''syntax:''' ''ngx.sleep(seconds)''\n\n'''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*''\n\nSleeps for the specified seconds without blocking. One can specify time resolution up to 0.001 seconds (i.e., one millisecond).\n\nBehind the scene, this method makes use of the Nginx timers.\n\nSince the <code>0.7.20</code> release, The <code>0</code> time argument can also be specified.\n\nThis method was introduced in the <code>0.5.0rc30</code> release.\n\n== ngx.escape_uri ==\n\n'''syntax:''' ''newstr = ngx.escape_uri(str, type?)''\n\n'''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*''\n\nSince `v0.10.16`, this function accepts an optional <code>type</code> argument.\nIt accepts the following values (defaults to `2`):\n\n* `0`: escapes <code>str</code> as a full URI. And the characters\n<code> </code> (space), <code>#</code>, <code>%</code>,\n`?`, 0x00 ~ 0x1F, 0x7F ~ 0xFF will be escaped.\n* `2`: escape <code>str</code> as a URI component. All characters except\nalphabetic characters, digits, <code>-</code>, <code>.</code>, <code>_</code>,\n<code>~</code> will be encoded as `%XX`.\n\n== ngx.unescape_uri ==\n\n'''syntax:''' ''newstr = ngx.unescape_uri(str)''\n\n'''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*''\n\nUnescape <code>str</code> as an escaped URI component.\n\nFor example,\n\n<geshi lang=\"lua\">\n    ngx.say(ngx.unescape_uri(\"b%20r56+7\"))\n</geshi>\n\ngives the output\n\n<geshi lang=\"text\">\n    b r56 7\n</geshi>\n\nInvalid 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.\n\nFor example,\n\n<geshi lang=\"lua\">\n    ngx.say(ngx.unescape_uri(\"try %search%%20%again%\"))\n</geshi>\n\ngives the output\n\n<geshi lang=\"text\">\n    try %search% %again%\n</geshi>\n\n(Note that `%20` following `%` got unescaped, even it can be considered a part of invalid sequence.)\n\n== ngx.encode_args ==\n\n'''syntax:''' ''str = ngx.encode_args(table)''\n\n'''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*''\n\nEncode the Lua table to a query args string according to the URI encoded rules.\n\nFor example,\n\n<geshi lang=\"lua\">\n    ngx.encode_args({foo = 3, [\"b r\"] = \"hello world\"})\n</geshi>\n\nyields\n\n<geshi lang=\"text\">\n    foo=3&b%20r=hello%20world\n</geshi>\n\nThe table keys must be Lua strings.\n\nMulti-value query args are also supported. Just use a Lua table for the argument's value, for example:\n\n<geshi lang=\"lua\">\n    ngx.encode_args({baz = {32, \"hello\"}})\n</geshi>\n\ngives\n\n<geshi lang=\"text\">\n    baz=32&baz=hello\n</geshi>\n\nIf the value table is empty and the effect is equivalent to the <code>nil</code> value.\n\nBoolean argument values are also supported, for instance,\n\n<geshi lang=\"lua\">\n    ngx.encode_args({a = true, b = 1})\n</geshi>\n\nyields\n\n<geshi lang=\"text\">\n    a&b=1\n</geshi>\n\nIf the argument value is <code>false</code>, then the effect is equivalent to the <code>nil</code> value.\n\nThis method was first introduced in the <code>v0.3.1rc27</code> release.\n\n== ngx.decode_args ==\n\n'''syntax:''' ''table, err = ngx.decode_args(str, max_args?)''\n\n'''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*''\n\nDecodes a URI encoded query-string into a Lua table. This is the inverse function of [[#ngx.encode_args|ngx.encode_args]].\n\nThe optional <code>max_args</code> argument can be used to specify the maximum number of arguments parsed from the <code>str</code> argument. By default, a maximum of 100 request arguments are parsed (including those with the same name) and that additional URI arguments are silently discarded to guard against potential denial of service attacks. Since <code>v0.10.13</code>, when the limit is exceeded, it will return a second value which is the string `\"truncated\"`.\n\nThis argument can be set to zero to remove the limit and to process all request arguments received:\n\n<geshi lang=\"lua\">\n    local args = ngx.decode_args(str, 0)\n</geshi>\n\nRemoving the <code>max_args</code> cap is strongly discouraged.\n\nThis method was introduced in the <code>v0.5.0rc29</code>.\n\n== ngx.encode_base64 ==\n\n'''syntax:''' ''newstr = ngx.encode_base64(str, no_padding?)''\n\n'''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*''\n\nEncodes <code>str</code> to a base64 digest.\n\nSince the <code>0.9.16</code> release, an optional boolean-typed <code>no_padding</code> argument can be specified to control whether the base64 padding should be appended to the resulting digest (default to <code>false</code>, i.e., with padding enabled).\n\n== ngx.decode_base64 ==\n\n'''syntax:''' ''newstr = ngx.decode_base64(str)''\n\n'''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*''\n\nDecodes the <code>str</code> argument as a base64 digest to the raw form.\nThe <code>str</code> should be standard 'base64' encoding for RFC 3548 or RFC 4648, and will returns <code>nil</code> if is not well formed or any characters not in the base encoding alphabet.\n\n== ngx.decode_base64mime ==\n'''syntax:''' ''newstr = ngx.decode_base64mime(str)''\n\n'''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*''\n\n'''requires:''' <code>resty.core.base64</code> or <code>resty.core</code>\n\nDecodes the <code>str</code> argument as a base64 digest to the raw form.\nThe <code>str</code> follows base64 transfer encoding for MIME (RFC 2045), and will discard characters outside the base encoding alphabet.\nReturns <code>nil</code> if <code>str</code> is not well formed.\n\n '''Note:''' This method requires the <code>resty.core.base64</code> or <code>resty.core</code> modules from the [https://github.com/openresty/lua-resty-core lua-resty-core] library.\n\n== ngx.crc32_short ==\n\n'''syntax:''' ''intval = ngx.crc32_short(str)''\n\n'''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*''\n\nCalculates the CRC-32 (Cyclic Redundancy Code) digest for the <code>str</code> argument.\n\nThis method performs better on relatively short <code>str</code> inputs (i.e., less than 30 ~ 60 bytes), as compared to [[#ngx.crc32_long|ngx.crc32_long]]. The result is exactly the same as [[#ngx.crc32_long|ngx.crc32_long]].\n\nBehind the scene, it is just a thin wrapper around the <code>ngx_crc32_short</code> function defined in the Nginx core.\n\nThis API was first introduced in the <code>v0.3.1rc8</code> release.\n\n== ngx.crc32_long ==\n\n'''syntax:''' ''intval = ngx.crc32_long(str)''\n\n'''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*''\n\nCalculates the CRC-32 (Cyclic Redundancy Code) digest for the <code>str</code> argument.\n\nThis method performs better on relatively long <code>str</code> inputs (i.e., longer than 30 ~ 60 bytes), as compared to [[#ngx.crc32_short|ngx.crc32_short]].  The result is exactly the same as [[#ngx.crc32_short|ngx.crc32_short]].\n\nBehind the scene, it is just a thin wrapper around the <code>ngx_crc32_long</code> function defined in the Nginx core.\n\nThis API was first introduced in the <code>v0.3.1rc8</code> release.\n\n== ngx.hmac_sha1 ==\n\n'''syntax:''' ''digest = ngx.hmac_sha1(secret_key, str)''\n\n'''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*''\n\nComputes the [https://en.wikipedia.org/wiki/HMAC HMAC-SHA1] digest of the argument <code>str</code> and turns the result using the secret key <code><secret_key></code>.\n\nThe raw binary form of the <code>HMAC-SHA1</code> digest will be generated, use [[#ngx.encode_base64|ngx.encode_base64]], for example, to encode the result to a textual representation if desired.\n\nFor example,\n\n<geshi lang=\"lua\">\n    local key = \"thisisverysecretstuff\"\n    local src = \"some string we want to sign\"\n    local digest = ngx.hmac_sha1(key, src)\n    ngx.say(ngx.encode_base64(digest))\n</geshi>\n\nyields the output\n\n<geshi lang=\"text\">\n    R/pvxzHC4NLtj7S+kXFg/NePTmk=\n</geshi>\n\nThis API requires the OpenSSL library enabled in the Nginx build (usually by passing the <code>--with-http_ssl_module</code> option to the <code>./configure</code> script).\n\nThis function was first introduced in the <code>v0.3.1rc29</code> release.\n\n== ngx.md5 ==\n\n'''syntax:''' ''digest = ngx.md5(str)''\n\n'''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*''\n\nReturns the hexadecimal representation of the MD5 digest of the <code>str</code> argument.\n\nFor example,\n\n<geshi lang=\"nginx\">\n    location = /md5 {\n        content_by_lua_block {\n            ngx.say(ngx.md5(\"hello\"))\n        }\n    }\n</geshi>\n\nyields the output\n\n<geshi lang=\"text\">\n    5d41402abc4b2a76b9719d911017c592\n</geshi>\n\nSee [[#ngx.md5_bin|ngx.md5_bin]] if the raw binary MD5 digest is required.\n\n== ngx.md5_bin ==\n\n'''syntax:''' ''digest = ngx.md5_bin(str)''\n\n'''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*''\n\nReturns the binary form of the MD5 digest of the <code>str</code> argument.\n\nSee [[#ngx.md5|ngx.md5]] if the hexadecimal form of the MD5 digest is required.\n\n== ngx.sha1_bin ==\n\n'''syntax:''' ''digest = ngx.sha1_bin(str)''\n\n'''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*''\n\nReturns the binary form of the SHA-1 digest of the <code>str</code> argument.\n\nThis function requires SHA-1 support in the Nginx build. (This usually just means OpenSSL should be installed while building Nginx).\n\nThis function was first introduced in the <code>v0.5.0rc6</code>.\n\n== ngx.quote_sql_str ==\n\n'''syntax:''' ''quoted_value = ngx.quote_sql_str(raw_value)''\n\n'''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*''\n\nReturns a quoted SQL string literal according to the MySQL quoting rules.\n\n== ngx.today ==\n\n'''syntax:''' ''str = ngx.today()''\n\n'''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*''\n\nReturns current date (in the format <code>yyyy-mm-dd</code>) from the Nginx cached time (no syscall involved unlike Lua's date library).\n\nThis is the local time.\n\n== ngx.time ==\n\n'''syntax:''' ''secs = ngx.time()''\n\n'''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*''\n\nReturns the elapsed seconds from the epoch for the current time stamp from the Nginx cached time (no syscall involved unlike Lua's date library).\n\nUpdates of the Nginx time cache can be forced by calling [[#ngx.update_time|ngx.update_time]] first.\n\n== ngx.now ==\n\n'''syntax:''' ''secs = ngx.now()''\n\n'''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*''\n\nReturns 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).\n\nYou can forcibly update the Nginx time cache by calling [[#ngx.update_time|ngx.update_time]] first.\n\nThis API was first introduced in <code>v0.3.1rc32</code>.\n\n== ngx.update_time ==\n\n'''syntax:''' ''ngx.update_time()''\n\n'''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*''\n\nForcibly updates the Nginx current time cache. This call involves a syscall and thus has some overhead, so do not abuse it.\n\nThis API was first introduced in <code>v0.3.1rc32</code>.\n\n== ngx.localtime ==\n\n'''syntax:''' ''str = ngx.localtime()''\n\n'''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*''\n\nReturns the current time stamp (in the format <code>yyyy-mm-dd hh:mm:ss</code>) 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).\n\nThis is the local time.\n\n== ngx.utctime ==\n\n'''syntax:''' ''str = ngx.utctime()''\n\n'''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*''\n\nReturns the current time stamp (in the format <code>yyyy-mm-dd hh:mm:ss</code>) 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).\n\nThis is the UTC time.\n\n== ngx.cookie_time ==\n\n'''syntax:''' ''str = ngx.cookie_time(sec)''\n\n'''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*''\n\nReturns a formatted string can be used as the cookie expiration time. The parameter <code>sec</code> is the time stamp in seconds (like those returned from [[#ngx.time|ngx.time]]).\n\n<geshi lang=\"nginx\">\n    ngx.say(ngx.cookie_time(1290079655))\n        -- yields \"Thu, 18-Nov-10 11:27:35 GMT\"\n</geshi>\n\n== ngx.http_time ==\n\n'''syntax:''' ''str = ngx.http_time(sec)''\n\n'''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*''\n\nReturns a formated string can be used as the http header time (for example, being used in <code>Last-Modified</code> header). The parameter <code>sec</code> is the time stamp in seconds (like those returned from [[#ngx.time|ngx.time]]).\n\n<geshi lang=\"nginx\">\n    ngx.say(ngx.http_time(1290079655))\n        -- yields \"Thu, 18 Nov 2010 11:27:35 GMT\"\n</geshi>\n\n== ngx.parse_http_time ==\n\n'''syntax:''' ''sec = ngx.parse_http_time(str)''\n\n'''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*''\n\nParse the http time string (as returned by [[#ngx.http_time|ngx.http_time]]) into seconds. Returns the seconds or <code>nil</code> if the input string is in bad forms.\n\n<geshi lang=\"nginx\">\n    local time = ngx.parse_http_time(\"Thu, 18 Nov 2010 11:27:35 GMT\")\n    if time == nil then\n        ...\n    end\n</geshi>\n\n== ngx.is_subrequest ==\n\n'''syntax:''' ''value = ngx.is_subrequest''\n\n'''context:''' ''set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*''\n\nReturns <code>true</code> if the current request is an Nginx subrequest, or <code>false</code> otherwise.\n\n== ngx.re.match ==\n\n'''syntax:''' ''captures, err = ngx.re.match(subject, regex, options?, ctx?, res_table?)''\n\n'''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*''\n\nMatches the <code>subject</code> string using the Perl compatible regular expression <code>regex</code> with the optional <code>options</code>.\n\nOnly the first occurrence of the match is returned, or <code>nil</code> if no match is found. In case of errors, like seeing a bad regular expression or exceeding the PCRE stack limit, <code>nil</code> and a string describing the error will be returned.\n\nWhen a match is found, a Lua table <code>captures</code> is returned, where <code>captures[0]</code> holds the whole substring being matched, and <code>captures[1]</code> holds the first parenthesized sub-pattern's capturing, <code>captures[2]</code> the second, and so on.\n\n<geshi lang=\"lua\">\n    local m, err = ngx.re.match(\"hello, 1234\", \"[0-9]+\")\n    if m then\n        -- m[0] == \"1234\"\n\n    else\n        if err then\n            ngx.log(ngx.ERR, \"error: \", err)\n            return\n        end\n\n        ngx.say(\"match not found\")\n    end\n</geshi>\n\n<geshi lang=\"lua\">\n    local m, err = ngx.re.match(\"hello, 1234\", \"([0-9])[0-9]+\")\n    -- m[0] == \"1234\"\n    -- m[1] == \"1\"\n</geshi>\n\nNamed captures are also supported since the <code>v0.7.14</code> release\nand are returned in the same Lua table as key-value pairs as the numbered captures.\n\n<geshi lang=\"lua\">\n    local m, err = ngx.re.match(\"hello, 1234\", \"([0-9])(?<remaining>[0-9]+)\")\n    -- m[0] == \"1234\"\n    -- m[1] == \"1\"\n    -- m[2] == \"234\"\n    -- m[\"remaining\"] == \"234\"\n</geshi>\n\nUnmatched subpatterns will have <code>false</code> values in their <code>captures</code> table fields.\n\n<geshi lang=\"lua\">\n    local m, err = ngx.re.match(\"hello, world\", \"(world)|(hello)|(?<named>howdy)\")\n    -- m[0] == \"hello\"\n    -- m[1] == false\n    -- m[2] == \"hello\"\n    -- m[3] == false\n    -- m[\"named\"] == false\n</geshi>\n\nSpecify <code>options</code> to control how the match operation will be performed. The following option characters are supported:\n\n<geshi lang=\"text\">\n    a             anchored mode (only match from the beginning)\n\n    d             enable the DFA mode (or the longest token match semantics).\n                  this requires PCRE 6.0+ or else a Lua exception will be thrown.\n                  first introduced in ngx_lua v0.3.1rc30.\n\n    D             enable duplicate named pattern support. This allows named\n                  subpattern names to be repeated, returning the captures in\n                  an array-like Lua table. for example,\n                    local m = ngx.re.match(\"hello, world\",\n                                           \"(?<named>\\w+), (?<named>\\w+)\",\n                                           \"D\")\n                    -- m[\"named\"] == {\"hello\", \"world\"}\n                  this option was first introduced in the v0.7.14 release.\n                  this option requires at least PCRE 8.12.\n\n    i             case insensitive mode (similar to Perl's /i modifier)\n\n    j             enable PCRE JIT compilation, this requires PCRE 8.21+ which\n                  must be built with the --enable-jit option. for optimum performance,\n                  this option should always be used together with the 'o' option.\n                  first introduced in ngx_lua v0.3.1rc30.\n\n    J             enable the PCRE Javascript compatible mode. this option was\n                  first introduced in the v0.7.14 release. this option requires\n                  at least PCRE 8.12.\n\n    m             multi-line mode (similar to Perl's /m modifier)\n\n    o             compile-once mode (similar to Perl's /o modifier),\n                  to enable the worker-process-level compiled-regex cache\n\n    s             single-line mode (similar to Perl's /s modifier)\n\n    u             UTF-8 mode. this requires PCRE to be built with\n                  the --enable-utf8 option or else a Lua exception will be thrown.\n\n    U             similar to \"u\" but disables PCRE's UTF-8 validity check on\n                  the subject string. first introduced in ngx_lua v0.8.1.\n\n    x             extended mode (similar to Perl's /x modifier)\n</geshi>\n\nThese options can be combined:\n\n<geshi lang=\"nginx\">\n    local m, err = ngx.re.match(\"hello, world\", \"HEL LO\", \"ix\")\n    -- m[0] == \"hello\"\n</geshi>\n\n<geshi lang=\"nginx\">\n    local m, err = ngx.re.match(\"hello, 美好生活\", \"HELLO, (.{2})\", \"iu\")\n    -- m[0] == \"hello, 美好\"\n    -- m[1] == \"美好\"\n</geshi>\n\nThe <code>o</code> option is useful for performance tuning, because the regex pattern in question will only be compiled once, cached in the worker-process level, and shared among all requests in the current Nginx worker process. The upper limit of the regex cache can be tuned via the [[#lua_regex_cache_max_entries|lua_regex_cache_max_entries]] directive.\n\nThe optional fourth argument, <code>ctx</code>, can be a Lua table holding an optional <code>pos</code> field. When the <code>pos</code> field in the <code>ctx</code> table argument is specified, <code>ngx.re.match</code> will start matching from that offset (starting from 1). Regardless of the presence of the <code>pos</code> field in the <code>ctx</code> table, <code>ngx.re.match</code> will always set this <code>pos</code> field to the position ''after'' the substring matched by the whole pattern in case of a successful match. When match fails, the <code>ctx</code> table will be left intact.\n\n<geshi lang=\"lua\">\n    local ctx = {}\n    local m, err = ngx.re.match(\"1234, hello\", \"[0-9]+\", \"\", ctx)\n         -- m[0] = \"1234\"\n         -- ctx.pos == 5\n</geshi>\n\n<geshi lang=\"lua\">\n    local ctx = { pos = 2 }\n    local m, err = ngx.re.match(\"1234, hello\", \"[0-9]+\", \"\", ctx)\n         -- m[0] = \"234\"\n         -- ctx.pos == 5\n</geshi>\n\nThe <code>ctx</code> table argument combined with the <code>a</code> regex modifier can be used to construct a lexer atop <code>ngx.re.match</code>.\n\nNote that, the <code>options</code> argument is not optional when the <code>ctx</code> argument is specified and that the empty Lua string (<code>\"\"</code>) must be used as placeholder for <code>options</code> if no meaningful regex options are required.\n\nThis method requires the PCRE library enabled in Nginx ([[#Special Escaping Sequences|Known Issue With Special Escaping Sequences]]).\n\nTo confirm that PCRE JIT is enabled, activate the Nginx debug log by adding the <code>--with-debug</code> option to Nginx or OpenResty's <code>./configure</code> script. Then, enable the \"debug\" error log level in <code>error_log</code> directive. The following message will be generated if PCRE JIT is enabled:\n\n<geshi lang=\"text\">\n    pcre JIT compiling result: 1\n</geshi>\n\nStarting from the <code>0.9.4</code> release, this function also accepts a 5th argument, <code>res_table</code>, for letting the caller supply the Lua table used to hold all the capturing results. Starting from <code>0.9.6</code>, it is the caller's responsibility to ensure this table is empty. This is very useful for recycling Lua tables and saving GC and table allocation overhead.\n\nThis feature was introduced in the <code>v0.2.1rc11</code> release.\n\n== ngx.re.find ==\n\n'''syntax:''' ''from, to, err = ngx.re.find(subject, regex, options?, ctx?, nth?)''\n\n'''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*''\n\nSimilar to [[#ngx.re.match|ngx.re.match]] but only returns the beginning index (<code>from</code>) and end index (<code>to</code>) 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.\n\nIn case of errors (like bad regexes or any PCRE runtime errors), this API function returns two <code>nil</code> values followed by a string describing the error.\n\nIf no match is found, this function just returns a <code>nil</code> value.\n\nBelow is an example:\n\n<geshi lang=\"lua\">\n    local s = \"hello, 1234\"\n    local from, to, err = ngx.re.find(s, \"([0-9]+)\", \"jo\")\n    if from then\n        ngx.say(\"from: \", from)\n        ngx.say(\"to: \", to)\n        ngx.say(\"matched: \", string.sub(s, from, to))\n    else\n        if err then\n            ngx.say(\"error: \", err)\n            return\n        end\n        ngx.say(\"not matched!\")\n    end\n</geshi>\n\nThis example produces the output\n\n    from: 8\n    to: 11\n    matched: 1234\n\nBecause this API function does not create new Lua strings nor new Lua tables, it is much faster than [[#ngx.re.match|ngx.re.match]]. It should be used wherever possible.\n\nSince the <code>0.9.3</code> release, an optional 5th argument, <code>nth</code>, is supported to specify which (submatch) capture's indexes to return. When <code>nth</code> is 0 (which is the default), the indexes for the whole matched substring is returned; when <code>nth</code> is 1, then the 1st submatch capture's indexes are returned; when <code>nth</code> is 2, then the 2nd submatch capture is returned, and so on. When the specified submatch does not have a match, then two <code>nil</code> values will be returned. Below is an example for this:\n\n<geshi lang=\"lua\">\n    local str = \"hello, 1234\"\n    local from, to = ngx.re.find(str, \"([0-9])([0-9]+)\", \"jo\", nil, 2)\n    if from then\n        ngx.say(\"matched 2nd submatch: \", string.sub(str, from, to))  -- yields \"234\"\n    end\n</geshi>\n\nThis API function was first introduced in the <code>v0.9.2</code> release.\n\n== ngx.re.gmatch ==\n\n'''syntax:''' ''iterator, err = ngx.re.gmatch(subject, regex, options?)''\n\n'''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*''\n\nSimilar 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 <code><subject></code> string argument with the PCRE <code>regex</code>.\n\nIn case of errors, like seeing an ill-formed regular expression, <code>nil</code> and a string describing the error will be returned.\n\nHere is a small example to demonstrate its basic usage:\n\n<geshi lang=\"lua\">\n    local iterator, err = ngx.re.gmatch(\"hello, world!\", \"([a-z]+)\", \"i\")\n    if not iterator then\n        ngx.log(ngx.ERR, \"error: \", err)\n        return\n    end\n\n    local m\n    m, err = iterator()    -- m[0] == m[1] == \"hello\"\n    if err then\n        ngx.log(ngx.ERR, \"error: \", err)\n        return\n    end\n\n    m, err = iterator()    -- m[0] == m[1] == \"world\"\n    if err then\n        ngx.log(ngx.ERR, \"error: \", err)\n        return\n    end\n\n    m, err = iterator()    -- m == nil\n    if err then\n        ngx.log(ngx.ERR, \"error: \", err)\n        return\n    end\n</geshi>\n\nMore often we just put it into a Lua loop:\n\n<geshi lang=\"lua\">\n    local it, err = ngx.re.gmatch(\"hello, world!\", \"([a-z]+)\", \"i\")\n    if not it then\n        ngx.log(ngx.ERR, \"error: \", err)\n        return\n    end\n\n    while true do\n        local m, err = it()\n        if err then\n            ngx.log(ngx.ERR, \"error: \", err)\n            return\n        end\n\n        if not m then\n            -- no match found (any more)\n            break\n        end\n\n        -- found a match\n        ngx.say(m[0])\n        ngx.say(m[1])\n    end\n</geshi>\n\nThe optional <code>options</code> argument takes exactly the same semantics as the [[#ngx.re.match|ngx.re.match]] method.\n\nThe current implementation requires that the iterator returned should only be used in a single request. That is, one should ''not'' assign it to a variable belonging to persistent namespace like a Lua package.\n\nThis method requires the PCRE library enabled in Nginx ([[#Special Escaping Sequences|Known Issue With Special Escaping Sequences]]).\n\nThis feature was first introduced in the <code>v0.2.1rc12</code> release.\n\n== ngx.re.sub ==\n\n'''syntax:''' ''newstr, n, err = ngx.re.sub(subject, regex, replace, options?)''\n\n'''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*''\n\nSubstitutes the first match of the Perl compatible regular expression <code>regex</code> on the <code>subject</code> argument string with the string or function argument <code>replace</code>. The optional <code>options</code> argument has exactly the same meaning as in [[#ngx.re.match|ngx.re.match]].\n\nThis method returns the resulting new string as well as the number of successful substitutions. In case of failures, like syntax errors in the regular expressions or the <code><replace></code> string argument, it will return <code>nil</code> and a string describing the error.\n\nWhen the <code>replace</code> is a string, then it is treated as a special template for string replacement. For example,\n\n<geshi lang=\"lua\">\n    local newstr, n, err = ngx.re.sub(\"hello, 1234\", \"([0-9])[0-9]\", \"[$0][$1]\")\n    if not newstr then\n        ngx.log(ngx.ERR, \"error: \", err)\n        return\n    end\n\n    -- newstr == \"hello, [12][1]34\"\n    -- n == 1\n</geshi>\n\nwhere <code>$0</code> referring to the whole substring matched by the pattern and <code>$1</code> referring to the first parenthesized capturing substring.\n\nCurly braces can also be used to disambiguate variable names from the background string literals:\n\n<geshi lang=\"lua\">\n    local newstr, n, err = ngx.re.sub(\"hello, 1234\", \"[0-9]\", \"${0}00\")\n    -- newstr == \"hello, 100234\"\n    -- n == 1\n</geshi>\n\nLiteral dollar sign characters (<code>$</code>) in the <code>replace</code> string argument can be escaped by another dollar sign, for instance,\n\n<geshi lang=\"lua\">\n    local newstr, n, err = ngx.re.sub(\"hello, 1234\", \"[0-9]\", \"$$\")\n    -- newstr == \"hello, $234\"\n    -- n == 1\n</geshi>\n\nDo not use backlashes to escape dollar signs; it will not work as expected.\n\nWhen the <code>replace</code> argument is of type \"function\", then it will be invoked with the \"match table\" as the argument to generate the replace string literal for substitution. The \"match table\" fed into the <code>replace</code> function is exactly the same as the return value of [[#ngx.re.match|ngx.re.match]]. Here is an example:\n\n<geshi lang=\"lua\">\n    local func = function (m)\n        return \"[\" .. m[0] .. \"][\" .. m[1] .. \"]\"\n    end\n\n    local newstr, n, err = ngx.re.sub(\"hello, 1234\", \"( [0-9] ) [0-9]\", func, \"x\")\n    -- newstr == \"hello, [12][1]34\"\n    -- n == 1\n</geshi>\n\nThe dollar sign characters in the return value of the <code>replace</code> function argument are not special at all.\n\nThis method requires the PCRE library enabled in Nginx ([[#Special Escaping Sequences|Known Issue With Special Escaping Sequences]]).\n\nThis feature was first introduced in the <code>v0.2.1rc13</code> release.\n\n== ngx.re.gsub ==\n\n'''syntax:''' ''newstr, n, err = ngx.re.gsub(subject, regex, replace, options?)''\n\n'''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*''\n\nJust like [[#ngx.re.sub|ngx.re.sub]], but does global substitution.\n\nHere is some examples:\n\n<geshi lang=\"lua\">\n    local newstr, n, err = ngx.re.gsub(\"hello, world\", \"([a-z])[a-z]+\", \"[$0,$1]\", \"i\")\n    if not newstr then\n        ngx.log(ngx.ERR, \"error: \", err)\n        return\n    end\n\n    -- newstr == \"[hello,h], [world,w]\"\n    -- n == 2\n</geshi>\n\n<geshi lang=\"lua\">\n    local func = function (m)\n        return \"[\" .. m[0] .. \",\" .. m[1] .. \"]\"\n    end\n    local newstr, n, err = ngx.re.gsub(\"hello, world\", \"([a-z])[a-z]+\", func, \"i\")\n    -- newstr == \"[hello,h], [world,w]\"\n    -- n == 2\n</geshi>\n\nThis method requires the PCRE library enabled in Nginx ([[#Special Escaping Sequences|Known Issue With Special Escaping Sequences]]).\n\nThis feature was first introduced in the <code>v0.2.1rc15</code> release.\n\n== ngx.shared.DICT ==\n\n'''syntax:''' ''dict = ngx.shared.DICT''\n\n'''syntax:''' ''dict = ngx.shared[name_var]''\n\n'''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*''\n\nFetching the shm-based Lua dictionary object for the shared memory zone named <code>DICT</code> defined by the [[#lua_shared_dict|lua_shared_dict]] directive.\n\nShared memory zones are always shared by all the Nginx worker processes in the current Nginx server instance.\n\nThe resulting object <code>dict</code> has the following methods:\n\n* [[#ngx.shared.DICT.get|get]]\n* [[#ngx.shared.DICT.get_stale|get_stale]]\n* [[#ngx.shared.DICT.set|set]]\n* [[#ngx.shared.DICT.safe_set|safe_set]]\n* [[#ngx.shared.DICT.add|add]]\n* [[#ngx.shared.DICT.safe_add|safe_add]]\n* [[#ngx.shared.DICT.replace|replace]]\n* [[#ngx.shared.DICT.delete|delete]]\n* [[#ngx.shared.DICT.incr|incr]]\n* [[#ngx.shared.DICT.lpush|lpush]]\n* [[#ngx.shared.DICT.rpush|rpush]]\n* [[#ngx.shared.DICT.lpop|lpop]]\n* [[#ngx.shared.DICT.rpop|rpop]]\n* [[#ngx.shared.DICT.llen|llen]]\n* [[#ngx.shared.DICT.ttl|ttl]]\n* [[#ngx.shared.DICT.expire|expire]]\n* [[#ngx.shared.DICT.flush_all|flush_all]]\n* [[#ngx.shared.DICT.flush_expired|flush_expired]]\n* [[#ngx.shared.DICT.get_keys|get_keys]]\n* [[#ngx.shared.DICT.capacity|capacity]]\n* [[#ngx.shared.DICT.free_space|free_space]]\n\nAll these methods are ''atomic'' operations, that is, safe from concurrent accesses from multiple Nginx worker processes for the same <code>lua_shared_dict</code> zone.\n\nHere is an example:\n\n<geshi lang=\"nginx\">\n    http {\n        lua_shared_dict dogs 10m;\n        server {\n            location /set {\n                content_by_lua_block {\n                    local dogs = ngx.shared.dogs\n                    dogs:set(\"Jim\", 8)\n                    ngx.say(\"STORED\")\n                }\n            }\n            location /get {\n                content_by_lua_block {\n                    local dogs = ngx.shared.dogs\n                    ngx.say(dogs:get(\"Jim\"))\n                }\n            }\n        }\n    }\n</geshi>\n\nLet us test it:\n\n<geshi lang=\"bash\">\n    $ curl localhost/set\n    STORED\n\n    $ curl localhost/get\n    8\n\n    $ curl localhost/get\n    8\n</geshi>\n\nThe number <code>8</code> will be consistently output when accessing <code>/get</code> regardless of how many Nginx workers there are because the <code>dogs</code> dictionary resides in the shared memory and visible to ''all'' of the worker processes.\n\nThe shared dictionary will retain its contents through a server config reload (either by sending the <code>HUP</code> signal to the Nginx process or by using the <code>-s reload</code> command-line option).\n\nThe contents in the dictionary storage will be lost, however, when the Nginx server quits.\n\nThis feature was first introduced in the <code>v0.3.1rc22</code> release.\n\n== ngx.shared.DICT.get ==\n\n'''syntax:''' ''value, flags = ngx.shared.DICT:get(key)''\n\n'''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*''\n\nRetrieving the value in the dictionary [[#ngx.shared.DICT|ngx.shared.DICT]] for the key <code>key</code>. If the key does not exist or has expired, then <code>nil</code> will be returned.\n\nIn case of errors, <code>nil</code> and a string describing the error will be returned.\n\nThe value returned will have the original data type when they were inserted into the dictionary, for example, Lua booleans, numbers, or strings.\n\nThe first argument to this method must be the dictionary object itself, for example,\n\n<geshi lang=\"lua\">\n    local cats = ngx.shared.cats\n    local value, flags = cats.get(cats, \"Marry\")\n</geshi>\n\nor use Lua's syntactic sugar for method calls:\n\n<geshi lang=\"lua\">\n    local cats = ngx.shared.cats\n    local value, flags = cats:get(\"Marry\")\n</geshi>\n\nThese two forms are fundamentally equivalent.\n\nIf the user flags is <code>0</code> (the default), then no flags value will be returned.\n\nThis feature was first introduced in the <code>v0.3.1rc22</code> release.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.get_stale ==\n\n'''syntax:''' ''value, flags, stale = ngx.shared.DICT:get_stale(key)''\n\n'''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*''\n\nSimilar to the [[#ngx.shared.DICT.get|get]] method but returns the value even if the key has already expired.\n\nReturns a 3rd value, <code>stale</code>, indicating whether the key has expired or not.\n\nNote that the value of an expired key is not guaranteed to be available so one should never rely on the availability of expired items.\n\nThis method was first introduced in the <code>0.8.6</code> release.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.set ==\n\n'''syntax:''' ''success, err, forcible = ngx.shared.DICT:set(key, value, exptime?, flags?)''\n\n'''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*''\n\nUnconditionally sets a key-value pair into the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]]. Returns three values:\n\n* <code>success</code>: boolean value to indicate whether the key-value pair is stored or not.\n* <code>err</code>: textual error message, can be <code>\"no memory\"</code>.\n* <code>forcible</code>: a boolean value to indicate whether other valid items have been removed forcibly when out of storage in the shared memory zone.\n\nThe <code>value</code> argument inserted can be Lua booleans, numbers, strings, or <code>nil</code>. Their value type will also be stored into the dictionary and the same data type can be retrieved later via the [[#ngx.shared.DICT.get|get]] method.\n\nThe optional <code>exptime</code> argument specifies expiration time (in seconds) for the inserted key-value pair. The time resolution is <code>0.001</code> seconds. If the <code>exptime</code> takes the value <code>0</code> (which is the default), then the item will never expire.\n\nThe optional <code>flags</code> argument specifies a user flags value associated with the entry to be stored. It can also be retrieved later with the value. The user flags is stored as an unsigned 32-bit integer internally. Defaults to <code>0</code>. The user flags argument was first introduced in the <code>v0.5.0rc2</code> release.\n\nWhen it fails to allocate memory for the current key-value item, then <code>set</code> will try removing existing items in the storage according to the Least-Recently Used (LRU) algorithm. Note that, LRU takes priority over expiration time here. If up to tens of existing items have been removed and the storage left is still insufficient (either due to the total capacity limit specified by [[#lua_shared_dict|lua_shared_dict]] or memory segmentation), then the <code>err</code> return value will be <code>no memory</code> and <code>success</code> will be <code>false</code>.\n\nIf the sizes of items in the dictionary are not multiples or even powers of a certain value (like 2), it is easier to encounter <code>no memory</code> error because of memory fragmentation. It is recommended to use different dictionaries for different sizes of items.\n\nWhen you encounter <code>no memory</code> error, you can also evict more least-recently-used items by retrying this method call more times to to make room for the current item.\n\nIf this method succeeds in storing the current item by forcibly removing other not-yet-expired items in the dictionary via LRU, the <code>forcible</code> return value will be <code>true</code>. If it stores the item without forcibly removing other valid items, then the return value <code>forcible</code> will be <code>false</code>.\n\nThe first argument to this method must be the dictionary object itself, for example,\n\n<geshi lang=\"lua\">\n    local cats = ngx.shared.cats\n    local succ, err, forcible = cats.set(cats, \"Marry\", \"it is a nice cat!\")\n</geshi>\n\nor use Lua's syntactic sugar for method calls:\n\n<geshi lang=\"lua\">\n    local cats = ngx.shared.cats\n    local succ, err, forcible = cats:set(\"Marry\", \"it is a nice cat!\")\n</geshi>\n\nThese two forms are fundamentally equivalent.\n\nThis feature was first introduced in the <code>v0.3.1rc22</code> release.\n\nPlease note that while internally the key-value pair is set atomically, the atomicity does not go across the method call boundary.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.safe_set ==\n\n'''syntax:''' ''ok, err = ngx.shared.DICT:safe_set(key, value, exptime?, flags?)''\n\n'''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*''\n\nSimilar 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 <code>nil</code> and the string \"no memory\".\n\nThis feature was first introduced in the <code>v0.7.18</code> release.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.add ==\n\n'''syntax:''' ''success, err, forcible = ngx.shared.DICT:add(key, value, exptime?, flags?)''\n\n'''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*''\n\nJust 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.\n\nIf the <code>key</code> argument already exists in the dictionary (and not expired for sure), the <code>success</code> return value will be <code>false</code> and the <code>err</code> return value will be <code>\"exists\"</code>.\n\nThis feature was first introduced in the <code>v0.3.1rc22</code> release.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.safe_add ==\n\n'''syntax:''' ''ok, err = ngx.shared.DICT:safe_add(key, value, exptime?, flags?)''\n\n'''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*''\n\nSimilar 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 <code>nil</code> and the string \"no memory\".\n\nThis feature was first introduced in the <code>v0.7.18</code> release.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.replace ==\n\n'''syntax:''' ''success, err, forcible = ngx.shared.DICT:replace(key, value, exptime?, flags?)''\n\n'''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*''\n\nJust 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.\n\nIf the <code>key</code> argument does ''not'' exist in the dictionary (or expired already), the <code>success</code> return value will be <code>false</code> and the <code>err</code> return value will be <code>\"not found\"</code>.\n\nThis feature was first introduced in the <code>v0.3.1rc22</code> release.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.delete ==\n\n'''syntax:''' ''ngx.shared.DICT:delete(key)''\n\n'''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*''\n\nUnconditionally removes the key-value pair from the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]].\n\nIt is equivalent to <code>ngx.shared.DICT:set(key, nil)</code>.\n\nThis feature was first introduced in the <code>v0.3.1rc22</code> release.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.incr ==\n\n'''syntax:''' ''newval, err, forcible? = ngx.shared.DICT:incr(key, value, init?, init_ttl?)''\n\n'''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*''\n\n'''optional requirement:''' <code>resty.core.shdict</code> or <code>resty.core</code>\n\nIncrements the (numerical) value for <code>key</code> in the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]] by the step value <code>value</code>. Returns the new resulting number if the operation is successfully completed or <code>nil</code> and an error message otherwise.\n\nWhen the key does not exist or has already expired in the shared dictionary,\n\n# if the <code>init</code> argument is not specified or takes the value <code>nil</code>, this method will return <code>nil</code> and the error string <code>\"not found\"</code>, or\n# if the <code>init</code> argument takes a number value, this method will create a new <code>key</code> with the value <code>init + value</code>.\n\nLike the [[#ngx.shared.DICT.add|add]] method, it also overrides the (least recently used) unexpired items in the store when running out of storage in the shared memory zone.\n\nThe optional <code>init_ttl</code> argument specifies expiration time (in seconds) of the value when it is initialized via the <code>init</code> argument. The time resolution is <code>0.001</code> seconds. If <code>init_ttl</code> takes the value <code>0</code> (which is the default), then the item will never expire. This argument cannot be provided without providing the <code>init</code> argument as well, and has no effect if the value already exists (e.g., if it was previously inserted via [[#ngx.shared.DICT.set|set]] or the likes).\n\n'''Note:''' Usage of the <code>init_ttl</code> argument requires the <code>resty.core.shdict</code> or <code>resty.core</code> modules from the [https://github.com/openresty/lua-resty-core lua-resty-core] library. Example:\n\n<geshi lang=\"lua\">\n    require \"resty.core\"\n\n    local cats = ngx.shared.cats\n    local newval, err = cats:incr(\"black_cats\", 1, 0, 0.1)\n\n    print(newval) -- 1\n\n    ngx.sleep(0.2)\n\n    local val, err = cats:get(\"black_cats\")\n    print(val) -- nil\n</geshi>\n\nThe <code>forcible</code> return value will always be <code>nil</code> when the <code>init</code> argument is not specified.\n\nIf this method succeeds in storing the current item by forcibly removing other not-yet-expired items in the dictionary via LRU, the <code>forcible</code> return value will be <code>true</code>. If it stores the item without forcibly removing other valid items, then the return value <code>forcible</code> will be <code>false</code>.\n\nIf the original value is not a valid Lua number in the dictionary, it will return <code>nil</code> and <code>\"not a number\"</code>.\n\nThe <code>value</code> argument and <code>init</code> argument can be any valid Lua numbers, like negative numbers or floating-point numbers.\n\nThis method was first introduced in the <code>v0.3.1rc22</code> release.\n\nThe optional <code>init</code> parameter was first added in the <code>v0.10.6</code> release.\n\nThe optional <code>init_ttl</code> parameter was introduced in the <code>v0.10.12rc2</code> release.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.lpush ==\n\n'''syntax:''' ''length, err = ngx.shared.DICT:lpush(key, value)''\n\n'''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*''\n\nInserts the specified (numerical or string) <code>value</code> at the head of the list named <code>key</code> in the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]]. Returns the number of elements in the list after the push operation.\n\nIf <code>key</code> does not exist, it is created as an empty list before performing the push operation. When the <code>key</code> already takes a value that is not a list, it will return <code>nil</code> and <code>\"value not a list\"</code>.\n\nIt 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 <code>nil</code> and the string \"no memory\".\n\nThis feature was first introduced in the <code>v0.10.6</code> release.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.rpush ==\n\n'''syntax:''' ''length, err = ngx.shared.DICT:rpush(key, value)''\n\n'''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*''\n\nSimilar to the [[#ngx.shared.DICT.lpush|lpush]] method, but inserts the specified (numerical or string) <code>value</code> at the tail of the list named <code>key</code>.\n\nThis feature was first introduced in the <code>v0.10.6</code> release.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.lpop ==\n\n'''syntax:''' ''val, err = ngx.shared.DICT:lpop(key)''\n\n'''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*''\n\nRemoves and returns the first element of the list named <code>key</code> in the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]].\n\nIf <code>key</code> does not exist, it will return <code>nil</code>. When the <code>key</code> already takes a value that is not a list, it will return <code>nil</code> and <code>\"value not a list\"</code>.\n\nThis feature was first introduced in the <code>v0.10.6</code> release.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.rpop ==\n\n'''syntax:''' ''val, err = ngx.shared.DICT:rpop(key)''\n\n'''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*''\n\nRemoves and returns the last element of the list named <code>key</code> in the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]].\n\nIf <code>key</code> does not exist, it will return <code>nil</code>. When the <code>key</code> already takes a value that is not a list, it will return <code>nil</code> and <code>\"value not a list\"</code>.\n\nThis feature was first introduced in the <code>v0.10.6</code> release.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.llen ==\n\n'''syntax:''' ''len, err = ngx.shared.DICT:llen(key)''\n\n'''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*''\n\nReturns the number of elements in the list named <code>key</code> in the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]].\n\nIf key does not exist, it is interpreted as an empty list and 0 is returned. When the <code>key</code> already takes a value that is not a list, it will return <code>nil</code> and <code>\"value not a list\"</code>.\n\nThis feature was first introduced in the <code>v0.10.6</code> release.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.ttl ==\n\n'''syntax:''' ''ttl, err = ngx.shared.DICT:ttl(key)''\n\n'''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*''\n\n'''requires:''' <code>resty.core.shdict</code> or <code>resty.core</code>\n\nRetrieves the remaining TTL (time-to-live in seconds) of a key-value pair in the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]]. Returns the TTL as a number if the operation is successfully completed or <code>nil</code> and an error message otherwise.\n\nIf the key does not exist (or has already expired), this method will return <code>nil</code> and the error string <code>\"not found\"</code>.\n\nThe TTL is originally determined by the <code>exptime</code> argument of the [[#ngx.shared.DICT.set|set]], [[#ngx.shared.DICT.add|add]], [[#ngx.shared.DICT.replace|replace]] (and the likes) methods. It has a time resolution of <code>0.001</code> seconds. A value of <code>0</code> means that the item will never expire.\n\nExample:\n\n<geshi lang=\"lua\">\n    require \"resty.core\"\n\n    local cats = ngx.shared.cats\n    local succ, err = cats:set(\"Marry\", \"a nice cat\", 0.5)\n\n    ngx.sleep(0.2)\n\n    local ttl, err = cats:ttl(\"Marry\")\n    ngx.say(ttl) -- 0.3\n</geshi>\n\nThis feature was first introduced in the <code>v0.10.11</code> release.\n\n'''Note:''' This method requires the <code>resty.core.shdict</code> or <code>resty.core</code> modules from the [https://github.com/openresty/lua-resty-core lua-resty-core] library.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.expire ==\n\n'''syntax:''' ''success, err = ngx.shared.DICT:expire(key, exptime)''\n\n'''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*''\n\n'''requires:''' <code>resty.core.shdict</code> or <code>resty.core</code>\n\nUpdates the <code>exptime</code> (in second) of a key-value pair in the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]]. Returns a boolean indicating success if the operation completes or <code>nil</code> and an error message otherwise.\n\nIf the key does not exist, this method will return <code>nil</code> and the error string <code>\"not found\"</code>.\n\nThe <code>exptime</code> argument has a resolution of <code>0.001</code> seconds. If <code>exptime</code> is <code>0</code>, then the item will never expire.\n\nExample:\n\n<geshi lang=\"lua\">\n    require \"resty.core\"\n\n    local cats = ngx.shared.cats\n    local succ, err = cats:set(\"Marry\", \"a nice cat\", 0.1)\n\n    succ, err = cats:expire(\"Marry\", 0.5)\n\n    ngx.sleep(0.2)\n\n    local val, err = cats:get(\"Marry\")\n    ngx.say(val) -- \"a nice cat\"\n</geshi>\n\nThis feature was first introduced in the <code>v0.10.11</code> release.\n\n'''Note:''' This method requires the <code>resty.core.shdict</code> or <code>resty.core</code> modules from the [https://github.com/openresty/lua-resty-core lua-resty-core] library.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.flush_all ==\n\n'''syntax:''' ''ngx.shared.DICT:flush_all()''\n\n'''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*''\n\nFlushes 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.\n\nThis feature was first introduced in the <code>v0.5.0rc17</code> release.\n\nSee also [[#ngx.shared.DICT.flush_expired|ngx.shared.DICT.flush_expired]] and [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.flush_expired ==\n\n'''syntax:''' ''flushed = ngx.shared.DICT:flush_expired(max_count?)''\n\n'''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*''\n\nFlushes out the expired items in the dictionary, up to the maximal number specified by the optional <code>max_count</code> argument. When the <code>max_count</code> argument is given <code>0</code> or not given at all, then it means unlimited. Returns the number of items that have actually been flushed.\n\nUnlike the [[#ngx.shared.DICT.flush_all|flush_all]] method, this method actually frees up the memory used by the expired items.\n\nThis feature was first introduced in the <code>v0.6.3</code> release.\n\nSee also [[#ngx.shared.DICT.flush_all|ngx.shared.DICT.flush_all]] and [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.get_keys ==\n\n'''syntax:''' ''keys = ngx.shared.DICT:get_keys(max_count?)''\n\n'''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*''\n\nFetch a list of the keys from the dictionary, up to <code><max_count></code>.\n\nBy default, only the first 1024 keys (if any) are returned. When the <code><max_count></code> argument is given the value <code>0</code>, then all the keys will be returned even there is more than 1024 keys in the dictionary.\n\n'''CAUTION''' Avoid calling this method on dictionaries with a very large number of keys as it may lock the dictionary for significant amount of time and block Nginx worker processes trying to access the dictionary.\n\nThis feature was first introduced in the <code>v0.7.3</code> release.\n\n== ngx.shared.DICT.capacity ==\n\n'''syntax:''' ''capacity_bytes = ngx.shared.DICT:capacity()''\n\n'''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*''\n\n'''requires:''' <code>resty.core.shdict</code> or <code>resty.core</code>\n\nRetrieves the capacity in bytes for the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]] declared with\nthe [[#lua_shared_dict|lua_shared_dict]] directive.\n\nExample:\n\n<geshi lang=\"lua\">\n    require \"resty.core.shdict\"\n\n    local cats = ngx.shared.cats\n    local capacity_bytes = cats:capacity()\n</geshi>\n\nThis feature was first introduced in the <code>v0.10.11</code> release.\n\n'''Note:''' This method requires the <code>resty.core.shdict</code> or <code>resty.core</code> modules from the [https://github.com/openresty/lua-resty-core lua-resty-core] library.\n\nThis feature requires at least Nginx core version <code>0.7.3</code>.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.shared.DICT.free_space ==\n\n'''syntax:''' ''free_page_bytes = ngx.shared.DICT:free_space()''\n\n'''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*''\n\n'''requires:''' <code>resty.core.shdict</code> or <code>resty.core</code>\n\nRetrieves the free page size in bytes for the shm-based dictionary [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n'''Note:''' The memory for ngx.shared.DICT is allocated via the Nginx slab allocator which has each slot for\ndata size ranges like \\~8, 9\\~16, 17\\~32, ..., 1025\\~2048, 2048\\~ bytes. And pages are assigned to a slot if there\nis no room in already assigned pages for the slot.\n\nSo even if the return value of the <code>free_space</code> method is zero, there may be room in already assigned pages, so\nyou may successfully set a new key value pair to the shared dict without getting <code>true</code> for <code>forcible</code> or\nnon nil <code>err</code> from the <code>ngx.shared.DICT.set</code>.\n\nOn the other hand, if already assigned pages for a slot are full and a new key value pair is added to the\nslot and there is no free page, you may get <code>true</code> for <code>forcible</code> or non nil <code>err</code> from the\n<code>ngx.shared.DICT.set</code> method.\n\nExample:\n\n<geshi lang=\"lua\">\n    require \"resty.core.shdict\"\n\n    local cats = ngx.shared.cats\n    local free_page_bytes = cats:free_space()\n</geshi>\n\nThis feature was first introduced in the <code>v0.10.11</code> release.\n\n'''Note:''' This method requires the <code>resty.core.shdict</code> or <code>resty.core</code> modules from the [https://github.com/openresty/lua-resty-core lua-resty-core] library.\n\nThis feature requires at least Nginx core version <code>1.11.7</code>.\n\nSee also [[#ngx.shared.DICT|ngx.shared.DICT]].\n\n== ngx.socket.udp ==\n\n'''syntax:''' ''udpsock = ngx.socket.udp()''\n\n'''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*''\n\nCreates 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:\n\n* [[#udpsock:setpeername|setpeername]]\n* [[#udpsock:send|send]]\n* [[#udpsock:receive|receive]]\n* [[#udpsock:close|close]]\n* [[#udpsock:settimeout|settimeout]]\n\nIt is intended to be compatible with the UDP API of the [http://w3.impa.br/~diego/software/luasocket/udp.html LuaSocket] library but is 100% nonblocking out of the box.\n\nThis feature was first introduced in the <code>v0.5.7</code> release.\n\nSee also [[#ngx.socket.tcp|ngx.socket.tcp]].\n\n== udpsock:setpeername ==\n\n'''syntax:''' ''ok, err = udpsock:setpeername(host, port)''\n\n'''syntax:''' ''ok, err = udpsock:setpeername(\"unix:/path/to/unix-domain.socket\")''\n\n'''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*''\n\nAttempts 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.\n\nBoth IP addresses and domain names can be specified as the <code>host</code> argument. In case of domain names, this method will use Nginx core's dynamic resolver to parse the domain name without blocking and it is required to configure the [[HttpCoreModule#resolver|resolver]] directive in the <code>nginx.conf</code> file like this:\n\n<geshi lang=\"nginx\">\n    resolver 8.8.8.8;  # use Google's public DNS nameserver\n</geshi>\n\nIf the nameserver returns multiple IP addresses for the host name, this method will pick up one randomly.\n\nIn case of error, the method returns <code>nil</code> followed by a string describing the error. In case of success, the method returns <code>1</code>.\n\nHere is an example for connecting to a UDP (memcached) server:\n\n<geshi lang=\"nginx\">\n    location /test {\n        resolver 8.8.8.8;\n\n        content_by_lua_block {\n            local sock = ngx.socket.udp()\n            local ok, err = sock:setpeername(\"my.memcached.server.domain\", 11211)\n            if not ok then\n                ngx.say(\"failed to connect to memcached: \", err)\n                return\n            end\n            ngx.say(\"successfully connected to memcached!\")\n            sock:close()\n        }\n    }\n</geshi>\n\nSince the <code>v0.7.18</code> release, connecting to a datagram unix domain socket file is also possible on Linux:\n\n<geshi lang=\"lua\">\n    local sock = ngx.socket.udp()\n    local ok, err = sock:setpeername(\"unix:/tmp/some-datagram-service.sock\")\n    if not ok then\n        ngx.say(\"failed to connect to the datagram unix domain socket: \", err)\n        return\n    end\n\n    -- do something after connect\n    -- such as sock:send or sock:receive\n</geshi>\n\nassuming the datagram service is listening on the unix domain socket file <code>/tmp/some-datagram-service.sock</code> and the client socket will use the \"autobind\" feature on Linux.\n\nCalling this method on an already connected socket object will cause the original connection to be closed first.\n\nThis method was first introduced in the <code>v0.5.7</code> release.\n\n== udpsock:send ==\n\n'''syntax:''' ''ok, err = udpsock:send(data)''\n\n'''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*''\n\nSends data on the current UDP or datagram unix domain socket object.\n\nIn case of success, it returns <code>1</code>. Otherwise, it returns <code>nil</code> and a string describing the error.\n\nThe input argument <code>data</code> can either be a Lua string or a (nested) Lua table holding string fragments. In case of table arguments, this method will copy all the string elements piece by piece to the underlying Nginx socket send buffers, which is usually optimal than doing string concatenation operations on the Lua land.\n\nThis feature was first introduced in the <code>v0.5.7</code> release.\n\n== udpsock:receive ==\n\n'''syntax:''' ''data, err = udpsock:receive(size?)''\n\n'''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*''\n\nReceives data from the UDP or datagram unix domain socket object with an optional receive buffer size argument, <code>size</code>.\n\nThis method is a synchronous operation and is 100% nonblocking.\n\nIn case of success, it returns the data received; in case of error, it returns <code>nil</code> with a string describing the error.\n\nIf the <code>size</code> argument is specified, then this method will use this size as the receive buffer size. But when this size is greater than <code>8192</code>, then <code>8192</code> will be used instead.\n\nIf no argument is specified, then the maximal buffer size, <code>8192</code> is assumed.\n\nTimeout for the reading operation is controlled by the [[#lua_socket_read_timeout|lua_socket_read_timeout]] config directive and the [[#udpsock:settimeout|settimeout]] method. And the latter takes priority. For example:\n\n<geshi lang=\"lua\">\n    sock:settimeout(1000)  -- one second timeout\n    local data, err = sock:receive()\n    if not data then\n        ngx.say(\"failed to read a packet: \", err)\n        return\n    end\n    ngx.say(\"successfully read a packet: \", data)\n</geshi>\n\nIt is important here to call the [[#udpsock:settimeout|settimeout]] method ''before'' calling this method.\n\nThis feature was first introduced in the <code>v0.5.7</code> release.\n\n== udpsock:close ==\n\n'''syntax:''' ''ok, err = udpsock:close()''\n\n'''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*''\n\nCloses the current UDP or datagram unix domain socket. It returns the <code>1</code> in case of success and returns <code>nil</code> with a string describing the error otherwise.\n\nSocket objects that have not invoked this method (and associated connections) will be closed when the socket object is released by the Lua GC (Garbage Collector) or the current client HTTP request finishes processing.\n\nThis feature was first introduced in the <code>v0.5.7</code> release.\n\n== udpsock:settimeout ==\n\n'''syntax:''' ''udpsock:settimeout(time)''\n\n'''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*''\n\nSet the timeout value in milliseconds for subsequent socket operations (like [[#udpsock:receive|receive]]).\n\nSettings done by this method takes priority over those config directives, like [[#lua_socket_read_timeout|lua_socket_read_timeout]].\n\nThis feature was first introduced in the <code>v0.5.7</code> release.\n\n== ngx.socket.stream ==\n\nJust an alias to [[#ngx.socket.tcp|ngx.socket.tcp]]. If the stream-typed cosocket may also connect to a unix domain\nsocket, then this API name is preferred.\n\nThis API function was first added to the <code>v0.10.1</code> release.\n\n== ngx.socket.tcp ==\n\n'''syntax:''' ''tcpsock = ngx.socket.tcp()''\n\n'''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*''\n\nCreates 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:\n\n* [[#tcpsock:bind|bind]]\n* [[#tcpsock:connect|connect]]\n* [[#tcpsock:setclientcert|setclientcert]]\n* [[#tcpsock:sslhandshake|sslhandshake]]\n* [[#tcpsock:send|send]]\n* [[#tcpsock:receive|receive]]\n* [[#tcpsock:close|close]]\n* [[#tcpsock:settimeout|settimeout]]\n* [[#tcpsock:settimeouts|settimeouts]]\n* [[#tcpsock:setoption|setoption]]\n* [[#tcpsock:receiveany|receiveany]]\n* [[#tcpsock:receiveuntil|receiveuntil]]\n* [[#tcpsock:setkeepalive|setkeepalive]]\n* [[#tcpsock:getreusedtimes|getreusedtimes]]\n\nIt is intended to be compatible with the TCP API of the [http://w3.impa.br/~diego/software/luasocket/tcp.html LuaSocket] library but is 100% nonblocking out of the box. Also, we introduce some new APIs to provide more functionalities.\n\nThe cosocket object created by this API function has exactly the same lifetime as the Lua handler creating it. So never pass the cosocket object to any other Lua handler (including ngx.timer callback functions) and never share the cosocket object between different Nginx requests.\n\nFor every cosocket object's underlying connection, if you do not\nexplicitly close it (via [[#tcpsock:close|close]]) or put it back to the connection\npool (via [[#tcpsock:setkeepalive|setkeepalive]]), then it is automatically closed when one of\nthe following two events happens:\n\n* the current request handler completes, or\n* the Lua cosocket object value gets collected by the Lua GC.\n\nFatal errors in cosocket operations always automatically close the current\nconnection (note that, read timeout error is the only error that is\nnot fatal), and if you call [[#tcpsock:close|close]] on a closed connection, you will get\nthe \"closed\" error.\n\nStarting from the <code>0.9.9</code> release, the cosocket object here is full-duplex, that is, a reader \"light thread\" and a writer \"light thread\" can operate on a single cosocket object simultaneously (both \"light threads\" must belong to the same Lua handler though, see reasons above). But you cannot have two \"light threads\" both reading (or writing or connecting) the same cosocket, otherwise you might get an error like \"socket busy reading\" when calling the methods of the cosocket object.\n\nThis feature was first introduced in the <code>v0.5.0rc1</code> release.\n\nSee also [[#ngx.socket.udp|ngx.socket.udp]].\n\n== tcpsock:bind ==\n'''syntax:''' ''ok, err = tcpsock:bind(address)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*''\n\nJust 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.\n\nOnly IP addresses can be specified as the <code>address</code> argument.\n\nHere is an example for connecting to a TCP server from the specified local IP address:\n\n<geshi lang=\"nginx\">\n    location /test {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            -- assume \"192.168.1.10\" is the local ip address\n            local ok, err = sock:bind(\"192.168.1.10\")\n            if not ok then\n                ngx.say(\"failed to bind\")\n                return\n            end\n            local ok, err = sock:connect(\"192.168.1.67\", 80)\n            if not ok then\n                ngx.say(\"failed to connect server: \", err)\n                return\n            end\n            ngx.say(\"successfully connected!\")\n            sock:close()\n        }\n    }\n</geshi>\n\n== tcpsock:connect ==\n\n'''syntax:''' ''ok, err = tcpsock:connect(host, port, options_table?)''\n\n'''syntax:''' ''ok, err = tcpsock:connect(\"unix:/path/to/unix-domain.socket\", options_table?)''\n\n'''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*''\n\nAttempts to connect a TCP socket object to a remote server or to a stream unix domain socket file without blocking.\n\nBefore actually resolving the host name and connecting to the remote backend, this method will always look up the connection pool for matched idle connections created by previous calls of this method (or the [[#ngx.socket.connect|ngx.socket.connect]] function).\n\nBoth IP addresses and domain names can be specified as the <code>host</code> argument. In case of domain names, this method will use Nginx core's dynamic resolver to parse the domain name without blocking and it is required to configure the [[HttpCoreModule#resolver|resolver]] directive in the <code>nginx.conf</code> file like this:\n\n<geshi lang=\"nginx\">\n    resolver 8.8.8.8;  # use Google's public DNS nameserver\n</geshi>\n\nIf the nameserver returns multiple IP addresses for the host name, this method will pick up one randomly.\n\nIn case of error, the method returns <code>nil</code> followed by a string describing the error. In case of success, the method returns <code>1</code>.\n\nHere is an example for connecting to a TCP server:\n\n<geshi lang=\"nginx\">\n    location /test {\n        resolver 8.8.8.8;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"www.google.com\", 80)\n            if not ok then\n                ngx.say(\"failed to connect to google: \", err)\n                return\n            end\n            ngx.say(\"successfully connected to google!\")\n            sock:close()\n        }\n    }\n</geshi>\n\nConnecting to a Unix Domain Socket file is also possible:\n\n<geshi lang=\"lua\">\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"unix:/tmp/memcached.sock\")\n    if not ok then\n        ngx.say(\"failed to connect to the memcached unix domain socket: \", err)\n        return\n    end\n\n    -- do something after connect\n    -- such as sock:send or sock:receive\n</geshi>\n\nassuming memcached (or something else) is listening on the unix domain socket file <code>/tmp/memcached.sock</code>.\n\nTimeout for the connecting operation is controlled by the [[#lua_socket_connect_timeout|lua_socket_connect_timeout]] config directive and the [[#tcpsock:settimeout|settimeout]] method. And the latter takes priority. For example:\n\n<geshi lang=\"lua\">\n    local sock = ngx.socket.tcp()\n    sock:settimeout(1000)  -- one second timeout\n    local ok, err = sock:connect(host, port)\n</geshi>\n\nIt is important here to call the [[#tcpsock:settimeout|settimeout]] method ''before'' calling this method.\n\nCalling this method on an already connected socket object will cause the original connection to be closed first.\n\nAn optional Lua table can be specified as the last argument to this method to specify various connect options:\n\n* <code>pool</code>\n: specify a custom name for the connection pool being used. If omitted, then the connection pool name will be generated from the string template <code>\"<host>:<port>\"</code> or <code>\"<unix-socket-path>\"</code>.\n\n* <code>pool_size</code>\n: specify the size of the connection pool. If omitted and no\n: <code>backlog</code> option was provided, no pool will be created. If omitted\n: but <code>backlog</code> was provided, the pool will be created with a default\n: size equal to the value of the [[#lua_socket_pool_size|lua_socket_pool_size]]\n: directive.\n: The connection pool holds up to <code>pool_size</code> alive connections\n: ready to be reused by subsequent calls to [[#tcpsock:connect|connect]], but\n: note that there is no upper limit to the total number of opened connections\n: outside of the pool. If you need to restrict the total number of opened\n: connections, specify the <code>backlog</code> option.\n: When the connection pool would exceed its size limit, the least recently used\n: (kept-alive) connection already in the pool will be closed to make room for\n: the current connection.\n: Note that the cosocket connection pool is per Nginx worker process rather\n: than per Nginx server instance, so the size limit specified here also applies\n: to every single Nginx worker process. Also note that the size of the connection\n: pool cannot be changed once it has been created.\n: This option was first introduced in the <code>v0.10.14</code> release.\n\n* <code>backlog</code>\n: if specified, this module will limit the total number of opened connections\n: for this pool. No more connections than <code>pool_size</code> can be opened\n: for this pool at any time. If the connection pool is full, subsequent\n: connect operations will be queued into a queue equal to this option's\n: value (the \"backlog\" queue).\n: If the number of queued connect operations is equal to <code>backlog</code>,\n: subsequent connect operations will fail and return <code>nil</code> plus the\n: error string <code>\"too many waiting connect operations\"</code>.\n: The queued connect operations will be resumed once the number of connections\n: in the pool is less than <code>pool_size</code>.\n: The queued connect operation will abort once they have been queued for more\n: than <code>connect_timeout</code>, controlled by\n: [[#tcpsock:settimeouts|settimeouts]], and will return <code>nil</code> plus\n: the error string <code>\"timeout\"</code>.\n: This option was first introduced in the <code>v0.10.14</code> release.\n\nThe support for the options table argument was first introduced in the <code>v0.5.7</code> release.\n\nThis method was first introduced in the <code>v0.5.0rc1</code> release.\n\n== tcpsock:setclientcert ==\n\n'''syntax:''' ''ok, err = tcpsock:setclientcert(cert, pkey)''\n\n'''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*''\n\nSet client certificate chain and corresponding private key to the TCP socket object.\nThe certificate chain and private key provided will be used later by the [tcpsock:sslhandshake](#tcpsocksslhandshake) method.\n\n* <code>cert</code> specify a client certificate chain cdata object that will be used while handshaking with\nremote 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)\nfunction provided by lua-resty-core. Note that specifying the <code>cert</code> option requires\ncorresponding <code>pkey</code> be provided too. See below.\n* <code>pkey</code> specify a private key corresponds to the <code>cert</code> option above.\nThese 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)\nfunction provided by lua-resty-core.\n\nIf both of <code>cert</code> and <code>pkey</code> are <code>nil</code>, this method will clear any existing client certificate and private key\nthat was previously set on the cosocket object.\n\nThis method was first introduced in the `v0.10.22` release.\n\n== tcpsock:sslhandshake ==\n\n'''syntax:''' ''session, err = tcpsock:sslhandshake(reused_session?, server_name?, ssl_verify?, send_status_req?)''\n\n'''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*''\n\nDoes SSL/TLS handshake on the currently established connection.\n\nThe optional <code>reused_session</code> argument can take a former SSL\nsession userdata returned by a previous <code>sslhandshake</code>\ncall for exactly the same target. For short-lived connections, reusing SSL\nsessions can usually speed up the handshake by one order by magnitude but it\nis not so useful if the connection pool is enabled. This argument defaults to\n<code>nil</code>. If this argument takes the boolean <code>false</code> value, no SSL session\nuserdata would return by this call and only a Lua boolean will be returned as\nthe first return value; otherwise the current SSL session will\nalways be returned as the first argument in case of successes.\n\nThe optional <code>server_name</code> argument is used to specify the server\nname for the new TLS extension Server Name Indication (SNI). Use of SNI can\nmake different servers share the same IP address on the server side. Also,\nwhen SSL verification is enabled, this <code>server_name</code> argument is\nalso used to validate the server name specified in the server certificate sent from\nthe remote.\n\nThe optional <code>ssl_verify</code> argument takes a Lua boolean value to\ncontrol whether to perform SSL verification. When set to <code>true</code>, the server\ncertificate will be verified according to the CA certificates specified by\nthe [[#lua_ssl_trusted_certificate|lua_ssl_trusted_certificate]] directive.\nYou may also need to adjust the [[#lua_ssl_verify_depth|lua_ssl_verify_depth]]\ndirective to control how deep we should follow along the certificate chain.\nAlso, when the <code>ssl_verify</code> argument is true and the\n<code>server_name</code> argument is also specified, the latter will be used\nto validate the server name in the server certificate.\n\nThe optional <code>send_status_req</code> argument takes a boolean that controls whether to send\nthe OCSP status request in the SSL handshake request (which is for requesting OCSP stapling).\n\nFor connections that have already done SSL/TLS handshake, this method returns\nimmediately.\n\nThis method was first introduced in the <code>v0.9.11</code> release.\n\n== tcpsock:send ==\n\n'''syntax:''' ''bytes, err = tcpsock:send(data)''\n\n'''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*''\n\nSends data without blocking on the current TCP or Unix Domain Socket connection.\n\nThis method is a synchronous operation that will not return until ''all'' the data has been flushed into the system socket send buffer or an error occurs.\n\nIn case of success, it returns the total number of bytes that have been sent. Otherwise, it returns <code>nil</code> and a string describing the error.\n\nThe input argument <code>data</code> can either be a Lua string or a (nested) Lua table holding string fragments. In case of table arguments, this method will copy all the string elements piece by piece to the underlying Nginx socket send buffers, which is usually optimal than doing string concatenation operations on the Lua land.\n\nTimeout for the sending operation is controlled by the [[#lua_socket_send_timeout|lua_socket_send_timeout]] config directive and the [[#tcpsock:settimeout|settimeout]] method. And the latter takes priority. For example:\n\n<geshi lang=\"lua\">\n    sock:settimeout(1000)  -- one second timeout\n    local bytes, err = sock:send(request)\n</geshi>\n\nIt is important here to call the [[#tcpsock:settimeout|settimeout]] method ''before'' calling this method.\n\nIn case of any connection errors, this method always automatically closes the current connection.\n\nThis feature was first introduced in the <code>v0.5.0rc1</code> release.\n\n== tcpsock:receive ==\n\n'''syntax:''' ''data, err, partial = tcpsock:receive(size)''\n\n'''syntax:''' ''data, err, partial = tcpsock:receive(pattern?)''\n\n'''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*''\n\nReceives data from the connected socket according to the reading pattern or size.\n\nThis method is a synchronous operation just like the [[#tcpsock:send|send]] method and is 100% nonblocking.\n\nIn case of success, it returns the data received; in case of error, it returns <code>nil</code> with a string describing the error and the partial data received so far.\n\nIf a number-like argument is specified (including strings that look like numbers), then it is interpreted as a size. This method will not return until it reads exactly this size of data or an error occurs.\n\nIf a non-number-like string argument is specified, then it is interpreted as a \"pattern\". The following patterns are supported:\n\n* <code>'*a'</code>: reads from the socket until the connection is closed. No end-of-line translation is performed;\n* <code>'*l'</code>: reads a line of text from the socket. The line is terminated by a <code>Line Feed</code> (LF) character (ASCII 10), optionally preceded by a <code>Carriage Return</code> (CR) character (ASCII 13). The CR and LF characters are not included in the returned line. In fact, all CR characters are ignored by the pattern.\n\nIf no argument is specified, then it is assumed to be the pattern <code>'*l'</code>, that is, the line reading pattern.\n\nTimeout for the reading operation is controlled by the [[#lua_socket_read_timeout|lua_socket_read_timeout]] config directive and the [[#tcpsock:settimeout|settimeout]] method. And the latter takes priority. For example:\n\n<geshi lang=\"lua\">\n    sock:settimeout(1000)  -- one second timeout\n    local line, err, partial = sock:receive()\n    if not line then\n        ngx.say(\"failed to read a line: \", err)\n        return\n    end\n    ngx.say(\"successfully read a line: \", line)\n</geshi>\n\nIt is important here to call the [[#tcpsock:settimeout|settimeout]] method ''before'' calling this method.\n\nSince the <code>v0.8.8</code> release, this method no longer automatically closes the current connection when the read timeout error happens. For other connection errors, this method always automatically closes the connection.\n\nThis feature was first introduced in the <code>v0.5.0rc1</code> release.\n\n== tcpsock:receiveany ==\n\n'''syntax:''' ''data, err = tcpsock:receiveany(max)''\n\n'''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*''\n\nReturns any data received by the connected socket, at most <code>max</code> bytes.\n\nThis method is a synchronous operation just like the [[#tcpsock:send|send]] method and is 100% nonblocking.\n\nIn case of success, it returns the data received; in case of error, it returns <code>nil</code> with a string describing the error.\n\nIf the received data is more than this size, this method will return with exactly this size of data.\nThe remaining data in the underlying receive buffer could be returned in the next reading operation.\n\nTimeout for the reading operation is controlled by the [[#lua_socket_read_timeout|lua_socket_read_timeout]] config directive and the [[#tcpsock:settimeouts|settimeouts]] method. And the latter takes priority. For example:\n\n<geshi lang=\"lua\">\n    sock:settimeouts(1000, 1000, 1000)  -- one second timeout for connect/read/write\n    local data, err = sock:receiveany(10 * 1024) -- read any data, at most 10K\n    if not data then\n        ngx.say(\"failed to read any data: \", err)\n        return\n    end\n    ngx.say(\"successfully read: \", data)\n</geshi>\n\nThis method doesn't automatically close the current connection when the read timeout error occurs. For other connection errors, this method always automatically closes the connection.\n\nThis feature was first introduced in the <code>v0.10.14</code> release.\n\n== tcpsock:receiveuntil ==\n\n'''syntax:''' ''iterator = tcpsock:receiveuntil(pattern, options?)''\n\n'''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*''\n\nThis 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.\n\nHere is an example for using this method to read a data stream with the boundary sequence <code>--abcedhb</code>:\n\n<geshi lang=\"lua\">\n    local reader = sock:receiveuntil(\"\\r\\n--abcedhb\")\n    local data, err, partial = reader()\n    if not data then\n        ngx.say(\"failed to read the data stream: \", err)\n    end\n    ngx.say(\"read the data stream: \", data)\n</geshi>\n\nWhen called without any argument, the iterator function returns the received data right ''before'' the specified pattern string in the incoming data stream. So for the example above, if the incoming data stream is <code>'hello, world! -agentzh\\r\\n--abcedhb blah blah'</code>, then the string <code>'hello, world! -agentzh'</code> will be returned.\n\nIn case of error, the iterator function will return <code>nil</code> along with a string describing the error and the partial data bytes that have been read so far.\n\nThe iterator function can be called multiple times and can be mixed safely with other cosocket method calls or other iterator function calls.\n\nThe iterator function behaves differently (i.e., like a real iterator) when it is called with a <code>size</code> argument. That is, it will read that <code>size</code> of data on each invocation and will return <code>nil</code> at the last invocation (either sees the boundary pattern or meets an error). For the last successful invocation of the iterator function, the <code>err</code> return value will be <code>nil</code> too. The iterator function will be reset after the last successful invocation that returns <code>nil</code> data and <code>nil</code> error. Consider the following example:\n\n<geshi lang=\"lua\">\n    local reader = sock:receiveuntil(\"\\r\\n--abcedhb\")\n\n    while true do\n        local data, err, partial = reader(4)\n        if not data then\n            if err then\n                ngx.say(\"failed to read the data stream: \", err)\n                break\n            end\n\n            ngx.say(\"read done\")\n            break\n        end\n        ngx.say(\"read chunk: [\", data, \"]\")\n    end\n</geshi>\n\nThen for the incoming data stream <code>'hello, world! -agentzh\\r\\n--abcedhb blah blah'</code>, we shall get the following output from the sample code above:\n\n<geshi lang=\"text\">\n    read chunk: [hell]\n    read chunk: [o, w]\n    read chunk: [orld]\n    read chunk: [! -a]\n    read chunk: [gent]\n    read chunk: [zh]\n    read done\n</geshi>\n\nNote that, the actual data returned ''might'' be a little longer than the size limit specified by the <code>size</code> argument when the boundary pattern has ambiguity for streaming parsing. Near the boundary of the data stream, the data string actually returned could also be shorter than the size limit.\n\nTimeout for the iterator function's reading operation is controlled by the [[#lua_socket_read_timeout|lua_socket_read_timeout]] config directive and the [[#tcpsock:settimeout|settimeout]] method. And the latter takes priority. For example:\n\n<geshi lang=\"lua\">\n    local readline = sock:receiveuntil(\"\\r\\n\")\n\n    sock:settimeout(1000)  -- one second timeout\n    line, err, partial = readline()\n    if not line then\n        ngx.say(\"failed to read a line: \", err)\n        return\n    end\n    ngx.say(\"successfully read a line: \", line)\n</geshi>\n\nIt is important here to call the [[#tcpsock:settimeout|settimeout]] method ''before'' calling the iterator function (note that the <code>receiveuntil</code> call is irrelevant here).\n\nAs from the <code>v0.5.1</code> release, this method also takes an optional <code>options</code> table argument to control the behavior. The following options are supported:\n\n* <code>inclusive</code>\n\nThe <code>inclusive</code> takes a boolean value to control whether to include the pattern string in the returned data string. Default to <code>false</code>. For example,\n\n<geshi lang=\"lua\">\n    local reader = tcpsock:receiveuntil(\"_END_\", { inclusive = true })\n    local data = reader()\n    ngx.say(data)\n</geshi>\n\nThen for the input data stream <code>\"hello world _END_ blah blah blah\"</code>, then the example above will output <code>hello world _END_</code>, including the pattern string <code>_END_</code> itself.\n\nSince the <code>v0.8.8</code> release, this method no longer automatically closes the current connection when the read timeout error happens. For other connection errors, this method always automatically closes the connection.\n\nThis method was first introduced in the <code>v0.5.0rc1</code> release.\n\n== tcpsock:close ==\n\n'''syntax:''' ''ok, err = tcpsock:close()''\n\n'''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*''\n\nCloses the current TCP or stream unix domain socket. It returns the <code>1</code> in case of success and returns <code>nil</code> with a string describing the error otherwise.\n\nNote that there is no need to call this method on socket objects that have invoked the [[#tcpsock:setkeepalive|setkeepalive]] method because the socket object is already closed (and the current connection is saved into the built-in connection pool).\n\nSocket objects that have not invoked this method (and associated connections) will be closed when the socket object is released by the Lua GC (Garbage Collector) or the current client HTTP request finishes processing.\n\nThis feature was first introduced in the <code>v0.5.0rc1</code> release.\n\n== tcpsock:settimeout ==\n\n'''syntax:''' ''tcpsock:settimeout(time)''\n\n'''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*''\n\nSet the timeout value in milliseconds for subsequent socket operations ([[#tcpsock:connect|connect]], [[#tcpsock:receive|receive]], and iterators returned from [[#tcpsock:receiveuntil|receiveuntil]]).\n\nSettings done by this method take priority over those specified via config directives (i.e. [[#lua_socket_connect_timeout|lua_socket_connect_timeout]], [[#lua_socket_send_timeout|lua_socket_send_timeout]], and [[#lua_socket_read_timeout|lua_socket_read_timeout]]).\n\nNote that this method does ''not'' affect the [[#lua_socket_keepalive_timeout|lua_socket_keepalive_timeout]] setting; the <code>timeout</code> argument to the [[#tcpsock:setkeepalive|setkeepalive]] method should be used for this purpose instead.\n\nThis feature was first introduced in the <code>v0.5.0rc1</code> release.\n\n== tcpsock:settimeouts ==\n\n'''syntax:''' ''tcpsock:settimeouts(connect_timeout, send_timeout, read_timeout)''\n\n'''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*''\n\nRespectively sets the connect, send, and read timeout thresholds (in milliseconds) for subsequent socket\noperations ([[#tcpsock:connect|connect]], [[#tcpsock:send|send]], [[#tcpsock:receive|receive]], and iterators returned from [[#tcpsock:receiveuntil|receiveuntil]]).\n\nSettings done by this method take priority over those specified via config directives (i.e. [[#lua_socket_connect_timeout|lua_socket_connect_timeout]], [[#lua_socket_send_timeout|lua_socket_send_timeout]], and [[#lua_socket_read_timeout|lua_socket_read_timeout]]).\n\nIt is recommended to use [[#tcpsock:settimeouts|settimeouts]] instead of [[#tcpsock:settimeout|settimeout]].\n\nNote that this method does ''not'' affect the [[#lua_socket_keepalive_timeout|lua_socket_keepalive_timeout]] setting; the <code>timeout</code> argument to the [[#tcpsock:setkeepalive|setkeepalive]] method should be used for this purpose instead.\n\nThis feature was first introduced in the <code>v0.10.7</code> release.\n\n== tcpsock:setoption ==\n\n'''syntax:''' ''ok, err = tcpsock:setoption(option, value?)''\n\n'''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*''\n\nThis 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 <code>v0.10.18</code>.\n\nThis feature was first introduced in the <code>v0.5.0rc1</code> release.\n\nIn case of success, it returns <code>true</code>. Otherwise, it returns nil and a string describing the error.\n\nThe <code>option</code> is a string with the option name, and the value depends on the option being set:\n\n* <code>keepalive</code>\n\n: Setting this option to true enables sending of keep-alive messages on\n: connection-oriented sockets. Make sure the <code>connect</code> function\n: had been called before, for example,\n\n    <geshi lang=\"lua\">\n    local ok, err = tcpsock:setoption(\"keepalive\", true)\n    if not ok then\n        ngx.say(\"setoption keepalive failed: \", err)\n    end\n    </geshi>\n* <code>reuseaddr</code>\n\n: Enabling this option indicates that the rules used in validating addresses\n: supplied in a call to bind should allow reuse of local addresses. Make sure\n: the <code>connect</code> function had been called before, for example,\n\n    <geshi lang=\"lua\">\n    local ok, err = tcpsock:setoption(\"reuseaddr\", 0)\n    if not ok then\n        ngx.say(\"setoption reuseaddr failed: \", err)\n    end\n    </geshi>\n* <code>tcp-nodelay</code>\n\n: Setting this option to true disables the Nagle's algorithm for the connection.\n: Make sure the <code>connect</code> function had been called before, for example,\n\n    <geshi lang=\"lua\">\n    local ok, err = tcpsock:setoption(\"tcp-nodelay\", true)\n    if not ok then\n        ngx.say(\"setoption tcp-nodelay failed: \", err)\n    end\n    </geshi>\n* <code>sndbuf</code>\n\n: Sets the maximum socket send buffer in bytes. The kernel doubles this value\n: (to allow space for bookkeeping overhead) when it is set using setsockopt().\n: Make sure the <code>connect</code> function had been called before, for example,\n\n    <geshi lang=\"lua\">\n    local ok, err = tcpsock:setoption(\"sndbuf\", 1024 * 10)\n    if not ok then\n        ngx.say(\"setoption sndbuf failed: \", err)\n    end\n    </geshi>\n* <code>rcvbuf</code>\n\n: Sets the maximum socket receive buffer in bytes. The kernel doubles this value\n: (to allow space for bookkeeping overhead) when it is set using setsockopt. Make\n: sure the <code>connect</code> function had been called before, for example,\n\n    <geshi lang=\"lua\">\n    local ok, err = tcpsock:setoption(\"rcvbuf\", 1024 * 10)\n    if not ok then\n        ngx.say(\"setoption rcvbuf failed: \", err)\n    end\n    </geshi>\n\nNOTE: Once the option is set, it will become effective until the connection is closed. If you know the connection is from the connection pool and all the in-pool connections already have called the setoption() method with the desired socket option state, then you can just skip calling setoption() again to avoid the overhead of repeated calls, for example,\n\n<geshi lang=\"lua\">\n    local count, err = tcpsock:getreusedtimes()\n    if not count then\n        ngx.say(\"getreusedtimes failed: \", err)\n        return\n    end\n\n    if count == 0 then\n        local ok, err = tcpsock:setoption(\"rcvbuf\", 1024 * 10)\n        if not ok then\n            ngx.say(\"setoption rcvbuf failed: \", err)\n            return\n        end\n    end\n</geshi>\n\nThese options described above are supported in <code>v0.10.18</code>, and more options will be implemented in future.\n\n== tcpsock:setkeepalive ==\n\n'''syntax:''' ''ok, err = tcpsock:setkeepalive(timeout?, size?)''\n\n'''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*''\n\nPuts 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.\n\nThe first optional argument, <code>timeout</code>, can be used to specify the maximal idle timeout (in milliseconds) for the current connection. If omitted, the default setting in the [[#lua_socket_keepalive_timeout|lua_socket_keepalive_timeout]] config directive will be used. If the <code>0</code> value is given, then the timeout interval is unlimited.\n\nThe second optional argument <code>size</code> is considered deprecated since\nthe <code>v0.10.14</code> release of this module, in favor of the\n<code>pool_size</code> option of the [[#tcpsock:connect|connect]] method.\nSince the <code>v0.10.14</code> release, this option will only take effect if\nthe call to [[#tcpsock:connect|connect]] did not already create a connection\npool.\nWhen this option takes effect (no connection pool was previously created by\n[[#tcpsock:connect|connect]]), it will specify the size of the connection pool,\nand create it.\nIf omitted (and no pool was previously created), the default size is the value\nof the [[#lua_socket_pool_size|lua_socket_pool_size]] directive.\nThe connection pool holds up to <code>size</code> alive connections ready to be\nreused by subsequent calls to [[#tcpsock:connect|connect]], but note that there\nis no upper limit to the total number of opened connections outside of the\npool.\nWhen the connection pool would exceed its size limit, the least recently used\n(kept-alive) connection already in the pool will be closed to make room for\nthe current connection.\nNote that the cosocket connection pool is per Nginx worker process rather\nthan per Nginx server instance, so the size limit specified here also applies\nto every single Nginx worker process. Also note that the size of the connection\npool cannot be changed once it has been created.\nIf you need to restrict the total number of opened connections, specify both\nthe <code>pool_size</code> and <code>backlog</code> option in the call to\n[[#tcpsock:connect|connect]].\n\nIn case of success, this method returns <code>1</code>; otherwise, it returns <code>nil</code> and a string describing the error.\n\nWhen the system receive buffer for the current connection has unread data, then this method will return the \"connection in dubious state\" error message (as the second return value) because the previous session has unread data left behind for the next session and the connection is not safe to be reused.\n\nThis method also makes the current cosocket object enter the \"closed\" state, so there is no need to manually call the [[#tcpsock:close|close]] method on it afterwards.\n\nThis feature was first introduced in the <code>v0.5.0rc1</code> release.\n\n== tcpsock:getreusedtimes ==\n\n'''syntax:''' ''count, err = tcpsock:getreusedtimes()''\n\n'''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*''\n\nThis method returns the (successfully) reused times for the current connection. In case of error, it returns <code>nil</code> and a string describing the error.\n\nIf the current connection does not come from the built-in connection pool, then this method always returns <code>0</code>, that is, the connection has never been reused (yet). If the connection comes from the connection pool, then the return value is always non-zero. So this method can also be used to determine if the current connection comes from the pool.\n\nThis feature was first introduced in the <code>v0.5.0rc1</code> release.\n\n== ngx.socket.connect ==\n\n'''syntax:''' ''tcpsock, err = ngx.socket.connect(host, port)''\n\n'''syntax:''' ''tcpsock, err = ngx.socket.connect(\"unix:/path/to/unix-domain.socket\")''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*''\n\nThis function is a shortcut for combining [[#ngx.socket.tcp|ngx.socket.tcp()]] and the [[#tcpsock:connect|connect()]] method call in a single operation. It is actually implemented like this:\n\n<geshi lang=\"lua\">\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(...)\n    if not ok then\n        return nil, err\n    end\n    return sock\n</geshi>\n\nThere is no way to use the [[#tcpsock:settimeout|settimeout]] method to specify connecting timeout for this method and the [[#lua_socket_connect_timeout|lua_socket_connect_timeout]] directive must be set at configure time instead.\n\nThis feature was first introduced in the <code>v0.5.0rc1</code> release.\n\n== ngx.get_phase ==\n\n'''syntax:''' ''str = ngx.get_phase()''\n\n'''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*''\n\nRetrieves the current running phase name. Possible return values are\n\n* <code>init</code>\n: for the context of [[#init_by_lua|init_by_lua*]].\n* <code>init_worker</code>\n: for the context of [[#init_worker_by_lua|init_worker_by_lua*]].\n* <code>ssl_cert</code>\n: for the context of [[#ssl_certificate_by_lua_block|ssl_certificate_by_lua*]].\n* <code>ssl_session_fetch</code>\n: for the context of [[#ssl_session_fetch_by_lua_block|ssl_session_fetch_by_lua*]].\n* <code>ssl_session_store</code>\n: for the context of [[#ssl_session_store_by_lua_block|ssl_session_store_by_lua*]].\n* <code>ssl_client_hello</code>\n: for the context of [[#ssl_client_hello_by_lua_block|ssl_client_hello_by_lua*]].\n* <code>set</code>\n: for the context of [[#set_by_lua|set_by_lua*]].\n* <code>rewrite</code>\n: for the context of [[#rewrite_by_lua|rewrite_by_lua*]].\n* <code>balancer</code>\n: for the context of [[#balancer_by_lua_block|balancer_by_lua*]].\n* <code>access</code>\n: for the context of [[#access_by_lua|access_by_lua*]].\n* <code>content</code>\n: for the context of [[#content_by_lua|content_by_lua*]].\n* <code>header_filter</code>\n: for the context of [[#header_filter_by_lua|header_filter_by_lua*]].\n* <code>body_filter</code>\n: for the context of [[#body_filter_by_lua|body_filter_by_lua*]].\n* <code>log</code>\n: for the context of [[#log_by_lua|log_by_lua*]].\n* <code>timer</code>\n: for the context of user callback functions for [[#ngx.timer.at|ngx.timer.*]].\n* <code>exit_worker</code>\n: for the context of [[#exit_worker_by_lua|exit_worker_by_lua*]].\n\nThis API was first introduced in the <code>v0.5.10</code> release.\n\n== ngx.thread.spawn ==\n\n'''syntax:''' ''co = ngx.thread.spawn(func, arg1, arg2, ...)''\n\n'''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*''\n\nSpawns a new user \"light thread\" with the Lua function <code>func</code> as well as those optional arguments <code>arg1</code>, <code>arg2</code>, and etc. Returns a Lua thread (or Lua coroutine) object represents this \"light thread\".\n\n\"Light threads\" are just a special kind of Lua coroutines that are scheduled by the ngx_lua module.\n\nBefore <code>ngx.thread.spawn</code> returns, the <code>func</code> will be called with those optional arguments until it returns, aborts with an error, or gets yielded due to I/O operations via the [[#Nginx API for Lua|Nginx API for Lua]] (like [[#tcpsock:receive|tcpsock:receive]]).\n\nAfter <code>ngx.thread.spawn</code> returns, the newly-created \"light thread\" will keep running asynchronously usually at various I/O events.\n\nAll the Lua code chunks running by [[#rewrite_by_lua|rewrite_by_lua]], [[#access_by_lua|access_by_lua]], and [[#content_by_lua|content_by_lua]] are in a boilerplate \"light thread\" created automatically by ngx_lua. Such boilerplate \"light thread\" are also called \"entry threads\".\n\nBy default, the corresponding Nginx handler (e.g., [[#rewrite_by_lua|rewrite_by_lua]] handler) will not terminate until\n\n# both the \"entry thread\" and all the user \"light threads\" terminates,\n# a \"light thread\" (either the \"entry thread\" or a user \"light thread\") aborts by calling [[#ngx.exit|ngx.exit]], [[#ngx.exec|ngx.exec]], [[#ngx.redirect|ngx.redirect]], or [[#ngx.req.set_uri|ngx.req.set_uri(uri, true)]], or\n# the \"entry thread\" terminates with a Lua error.\n\nWhen the user \"light thread\" terminates with a Lua error, however, it will not abort other running \"light threads\" like the \"entry thread\" does.\n\nDue 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|ngx.thread.wait]] 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|ngx.exit]] with and only with the status code <code>ngx.ERROR</code> (-1), <code>408</code>, <code>444</code>, or <code>499</code>.\n\nThe \"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\n\n# a (nonblocking) I/O operation cannot be completed in a single run,\n# it calls [[#coroutine.yield|coroutine.yield]] to actively give up execution, or\n# it is aborted by a Lua error or an invocation of [[#ngx.exit|ngx.exit]], [[#ngx.exec|ngx.exec]], [[#ngx.redirect|ngx.redirect]], or [[#ngx.req.set_uri|ngx.req.set_uri(uri, true)]].\n\nFor the first two cases, the \"light thread\" will usually be resumed later by the ngx_lua scheduler unless a \"stop-the-world\" event happens.\n\nUser \"light threads\" can create \"light threads\" themselves. And normal user coroutines created by [[#coroutine.create|coroutine.create]] can also create \"light threads\". The coroutine (be it a normal Lua coroutine or a \"light thread\") that directly spawns the \"light thread\" is called the \"parent coroutine\" for the \"light thread\" newly spawned.\n\nThe \"parent coroutine\" can call [[#ngx.thread.wait|ngx.thread.wait]] to wait on the termination of its child \"light thread\".\n\nYou can call coroutine.status() and coroutine.yield() on the \"light thread\" coroutines.\n\nThe status of the \"light thread\" coroutine can be \"zombie\" if\n\n# the current \"light thread\" already terminates (either successfully or with an error),\n# its parent coroutine is still alive, and\n# its parent coroutine is not waiting on it with [[#ngx.thread.wait|ngx.thread.wait]].\n\nThe following example demonstrates the use of coroutine.yield() in the \"light thread\" coroutines\nto do manual time-slicing:\n\n<geshi lang=\"lua\">\n    local yield = coroutine.yield\n\n    function f()\n        local self = coroutine.running()\n        ngx.say(\"f 1\")\n        yield(self)\n        ngx.say(\"f 2\")\n        yield(self)\n        ngx.say(\"f 3\")\n    end\n\n    local self = coroutine.running()\n    ngx.say(\"0\")\n    yield(self)\n\n    ngx.say(\"1\")\n    ngx.thread.spawn(f)\n\n    ngx.say(\"2\")\n    yield(self)\n\n    ngx.say(\"3\")\n    yield(self)\n\n    ngx.say(\"4\")\n</geshi>\n\nThen it will generate the output\n\n<geshi lang=\"text\">\n    0\n    1\n    f 1\n    2\n    f 2\n    3\n    f 3\n    4\n</geshi>\n\n\"Light threads\" are mostly useful for making concurrent upstream requests in a single Nginx request handler, much like a generalized version of [[#ngx.location.capture_multi|ngx.location.capture_multi]] that can work with all the [[#Nginx API for Lua|Nginx API for Lua]]. The following example demonstrates parallel requests to MySQL, Memcached, and upstream HTTP services in a single Lua handler, and outputting the results in the order that they actually return (similar to Facebook's BigPipe model):\n\n<geshi lang=\"lua\">\n    -- query mysql, memcached, and a remote http service at the same time,\n    -- output the results in the order that they\n    -- actually return the results.\n\n    local mysql = require \"resty.mysql\"\n    local memcached = require \"resty.memcached\"\n\n    local function query_mysql()\n        local db = mysql:new()\n        db:connect{\n                    host = \"127.0.0.1\",\n                    port = 3306,\n                    database = \"test\",\n                    user = \"monty\",\n                    password = \"mypass\"\n                  }\n        local res, err, errno, sqlstate =\n                db:query(\"select * from cats order by id asc\")\n        db:set_keepalive(0, 100)\n        ngx.say(\"mysql done: \", cjson.encode(res))\n    end\n\n    local function query_memcached()\n        local memc = memcached:new()\n        memc:connect(\"127.0.0.1\", 11211)\n        local res, err = memc:get(\"some_key\")\n        ngx.say(\"memcached done: \", res)\n    end\n\n    local function query_http()\n        local res = ngx.location.capture(\"/my-http-proxy\")\n        ngx.say(\"http done: \", res.body)\n    end\n\n    ngx.thread.spawn(query_mysql)      -- create thread 1\n    ngx.thread.spawn(query_memcached)  -- create thread 2\n    ngx.thread.spawn(query_http)       -- create thread 3\n</geshi>\n\nThis API was first enabled in the <code>v0.7.0</code> release.\n\n== ngx.thread.wait ==\n\n'''syntax:''' ''ok, res1, res2, ... = ngx.thread.wait(thread1, thread2, ...)''\n\n'''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*''\n\nWaits on one or more child \"light threads\" and returns the results of the first \"light thread\" that terminates (either successfully or with an error).\n\nThe arguments <code>thread1</code>, <code>thread2</code>, and etc are the Lua thread objects returned by earlier calls of [[#ngx.thread.spawn|ngx.thread.spawn]].\n\nThe return values have exactly the same meaning as [[#coroutine.resume|coroutine.resume]], that is, the first value returned is a boolean value indicating whether the \"light thread\" terminates successfully or not, and subsequent values returned are the return values of the user Lua function that was used to spawn the \"light thread\" (in case of success) or the error object (in case of failure).\n\nOnly the direct \"parent coroutine\" can wait on its child \"light thread\", otherwise a Lua exception will be raised.\n\nThe following example demonstrates the use of <code>ngx.thread.wait</code> and [[#ngx.location.capture|ngx.location.capture]] to emulate [[#ngx.location.capture_multi|ngx.location.capture_multi]]:\n\n<geshi lang=\"lua\">\n    local capture = ngx.location.capture\n    local spawn = ngx.thread.spawn\n    local wait = ngx.thread.wait\n    local say = ngx.say\n\n    local function fetch(uri)\n        return capture(uri)\n    end\n\n    local threads = {\n        spawn(fetch, \"/foo\"),\n        spawn(fetch, \"/bar\"),\n        spawn(fetch, \"/baz\")\n    }\n\n    for i = 1, #threads do\n        local ok, res = wait(threads[i])\n        if not ok then\n            say(i, \": failed to run: \", res)\n        else\n            say(i, \": status: \", res.status)\n            say(i, \": body: \", res.body)\n        end\n    end\n</geshi>\n\nHere it essentially implements the \"wait all\" model.\n\nAnd below is an example demonstrating the \"wait any\" model:\n\n<geshi lang=\"lua\">\n    function f()\n        ngx.sleep(0.2)\n        ngx.say(\"f: hello\")\n        return \"f done\"\n    end\n\n    function g()\n        ngx.sleep(0.1)\n        ngx.say(\"g: hello\")\n        return \"g done\"\n    end\n\n    local tf, err = ngx.thread.spawn(f)\n    if not tf then\n        ngx.say(\"failed to spawn thread f: \", err)\n        return\n    end\n\n    ngx.say(\"f thread created: \", coroutine.status(tf))\n\n    local tg, err = ngx.thread.spawn(g)\n    if not tg then\n        ngx.say(\"failed to spawn thread g: \", err)\n        return\n    end\n\n    ngx.say(\"g thread created: \", coroutine.status(tg))\n\n    ok, res = ngx.thread.wait(tf, tg)\n    if not ok then\n        ngx.say(\"failed to wait: \", res)\n        return\n    end\n\n    ngx.say(\"res: \", res)\n\n    -- stop the \"world\", aborting other running threads\n    ngx.exit(ngx.OK)\n</geshi>\n\nAnd it will generate the following output:\n\n<geshi lang=\"text\">\n    f thread created: running\n    g thread created: running\n    g: hello\n    res: g done\n</geshi>\n\nThis API was first enabled in the <code>v0.7.0</code> release.\n\n== ngx.thread.kill ==\n\n'''syntax:''' ''ok, err = ngx.thread.kill(thread)''\n\n'''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*''\n\nKills a running \"light thread\" created by [[#ngx.thread.spawn|ngx.thread.spawn]]. Returns a true value when successful or <code>nil</code> and a string describing the error otherwise.\n\nAccording to the current implementation, only the parent coroutine (or \"light thread\") can kill a thread. Also, a running \"light thread\" with pending Nginx subrequests (initiated by [[#ngx.location.capture|ngx.location.capture]] for example) cannot be killed due to a limitation in the Nginx core.\n\nThis API was first enabled in the <code>v0.9.9</code> release.\n\n== ngx.on_abort ==\n\n'''syntax:''' ''ok, err = ngx.on_abort(callback)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\nRegisters a user Lua function as the callback which gets called automatically when the client closes the (downstream) connection prematurely.\n\nReturns <code>1</code> if the callback is registered successfully or returns <code>nil</code> and a string describing the error otherwise.\n\nAll the [[#Nginx API for Lua|Nginx API for Lua]] can be used in the callback function because the function is run in a special \"light thread\", just as those \"light threads\" created by [[#ngx.thread.spawn|ngx.thread.spawn]].\n\nThe callback function can decide what to do with the client abortion event all by itself. For example, it can simply ignore the event by doing nothing and the current Lua request handler will continue executing without interruptions. And the callback function can also decide to terminate everything by calling [[#ngx.exit|ngx.exit]], for example,\n\n<geshi lang=\"lua\">\n    local function my_cleanup()\n        -- custom cleanup work goes here, like cancelling a pending DB transaction\n\n        -- now abort all the \"light threads\" running in the current request handler\n        ngx.exit(499)\n    end\n\n    local ok, err = ngx.on_abort(my_cleanup)\n    if not ok then\n        ngx.log(ngx.ERR, \"failed to register the on_abort callback: \", err)\n        ngx.exit(500)\n    end\n</geshi>\n\nWhen [[#lua_check_client_abort|lua_check_client_abort]] is set to <code>off</code> (which is the default), then this function call will always return the error message \"lua_check_client_abort is off\".\n\nAccording to the current implementation, this function can only be called once in a single request handler; subsequent calls will return the error message \"duplicate call\".\n\nThis API was first introduced in the <code>v0.7.4</code> release.\n\nSee also [[#lua_check_client_abort|lua_check_client_abort]].\n\n== ngx.timer.at ==\n\n'''syntax:''' ''hdl, err = ngx.timer.at(delay, callback, user_arg1, user_arg2, ...)''\n\n'''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*''\n\nCreates an Nginx timer with a user callback function as well as optional user arguments.\n\nThe first argument, <code>delay</code>, specifies the delay for the timer,\nin seconds. One can specify fractional seconds like <code>0.001</code> to mean 1\nmillisecond here. <code>0</code> delay can also be specified, in which case the\ntimer will immediately expire when the current handler yields\nexecution.\n\nThe second argument, <code>callback</code>, can\nbe any Lua function, which will be invoked later in a background\n\"light thread\" after the delay specified. The user callback will be\ncalled automatically by the Nginx core with the arguments <code>premature</code>,\n<code>user_arg1</code>, <code>user_arg2</code>, and etc, where the <code>premature</code>\nargument takes a boolean value indicating whether it is a premature timer\nexpiration or not, and <code>user_arg1</code>, <code>user_arg2</code>, and etc, are\nthose (extra) user arguments specified when calling <code>ngx.timer.at</code>\nas the remaining arguments.\n\nPremature timer expiration happens when the Nginx worker process is\ntrying to shut down, as in an Nginx configuration reload triggered by\nthe <code>HUP</code> signal or in an Nginx server shutdown. When the Nginx worker\nis trying to shut down, one can no longer call <code>ngx.timer.at</code> to\ncreate new timers with nonzero delays and in that case <code>ngx.timer.at</code> will return a \"conditional false\" value and\na string describing the error, that is, \"process exiting\".\n\nStarting from the <code>v0.9.3</code> release, it is allowed to create zero-delay timers even when the Nginx worker process starts shutting down.\n\nWhen a timer expires, the user Lua code in the timer callback is\nrunning in a \"light thread\" detached completely from the original\nrequest creating the timer. So objects with the same lifetime as the\nrequest creating them, like [[#ngx.socket.tcp|cosockets]], cannot be shared between the\noriginal request and the timer user callback function.\n\nHere is a simple example:\n\n<geshi lang=\"nginx\">\n    location / {\n        ...\n        log_by_lua_block {\n            local function push_data(premature, uri, args, status)\n                -- push the data uri, args, and status to the remote\n                -- via ngx.socket.tcp or ngx.socket.udp\n                -- (one may want to buffer the data in Lua a bit to\n                -- save I/O operations)\n            end\n            local ok, err = ngx.timer.at(0, push_data,\n                                         ngx.var.uri, ngx.var.args, ngx.header.status)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to create timer: \", err)\n                return\n            end\n\n            -- other job in log_by_lua_block\n        }\n    }\n</geshi>\n\nOne can also create infinite re-occurring timers, for instance, a timer getting triggered every <code>5</code> seconds, by calling <code>ngx.timer.at</code> recursively in the timer callback function. Here is such an example,\n\n<geshi lang=\"lua\">\n    local delay = 5\n    local handler\n    handler = function (premature)\n        -- do some routine job in Lua just like a cron job\n        if premature then\n            return\n        end\n        local ok, err = ngx.timer.at(delay, handler)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to create the timer: \", err)\n            return\n        end\n\n        -- do something in timer\n    end\n\n    local ok, err = ngx.timer.at(delay, handler)\n    if not ok then\n        ngx.log(ngx.ERR, \"failed to create the timer: \", err)\n        return\n    end\n\n    -- do other jobs\n</geshi>\n\nIt is recommended, however, to use the [[#ngx.timer.every|ngx.timer.every]] API function\ninstead for creating recurring timers since it is more robust.\n\nBecause timer callbacks run in the background and their running time\nwill not add to any client request's response time, they can easily\naccumulate in the server and exhaust system resources due to either\nLua programming mistakes or just too much client traffic. To prevent\nextreme consequences like crashing the Nginx server, there are\nbuilt-in limitations on both the number of \"pending timers\" and the\nnumber of \"running timers\" in an Nginx worker process. The \"pending\ntimers\" here mean timers that have not yet been expired and \"running\ntimers\" are those whose user callbacks are currently running.\n\nThe maximal number of pending timers allowed in an Nginx\nworker is controlled by the [[#lua_max_pending_timers|lua_max_pending_timers]]\ndirective. The maximal number of running timers is controlled by the\n[[#lua_max_running_timers|lua_max_running_timers]] directive.\n\nAccording to the current implementation, each \"running timer\" will\ntake one (fake) connection record from the global connection record\nlist configured by the standard [[EventsModule#worker_connections|worker_connections]] directive in\n<code>nginx.conf</code>. So ensure that the\n[[EventsModule#worker_connections|worker_connections]] directive is set to\na large enough value that takes into account both the real connections\nand fake connections required by timer callbacks (as limited by the\n[[#lua_max_running_timers|lua_max_running_timers]] directive).\n\nA lot of the Lua APIs for Nginx are enabled in the context of the timer\ncallbacks, like stream/datagram cosockets ([[#ngx.socket.tcp|ngx.socket.tcp]] and [[#ngx.socket.udp|ngx.socket.udp]]), shared\nmemory dictionaries ([[#ngx.shared.DICT|ngx.shared.DICT]]), user coroutines ([[#coroutine.create|coroutine.*]]),\nuser \"light threads\" ([[#ngx.thread.spawn|ngx.thread.*]]), [[#ngx.exit|ngx.exit]], [[#ngx.now|ngx.now]]/[[#ngx.time|ngx.time]],\n[[#ngx.md5|ngx.md5]]/[[#ngx.sha1_bin|ngx.sha1_bin]], are all allowed. But the subrequest API (like\n[[#ngx.location.capture|ngx.location.capture]]), the [[#ngx.req.start_time|ngx.req.*]] API, the downstream output API\n(like [[#ngx.say|ngx.say]], [[#ngx.print|ngx.print]], and [[#ngx.flush|ngx.flush]]) are explicitly disabled in\nthis context.\n\nYou 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.\n\nYou 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.\n\nPlease note that the timer Lua handler has its own copy of the `ngx.ctx` magic\ntable. It won't share the same `ngx.ctx` with the Lua handler creating the timer.\nIf you need to pass data from the timer creator to the timer handler, please\nuse the extra parameters of `ngx.timer.at()`.\n\nThis API was first introduced in the <code>v0.8.0</code> release.\n\n== ngx.timer.every ==\n\n'''syntax:''' ''hdl, err = ngx.timer.every(delay, callback, user_arg1, user_arg2, ...)''\n\n'''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*''\n\nSimilar to the [[#ngx.timer.at|ngx.timer.at]] API function, but\n\n# <code>delay</code> ''cannot'' be zero,\n# timer will be created every <code>delay</code> seconds until the current Nginx worker process starts exiting.\n\nLike [[#ngx.timer.at|ngx.timer.at]], the <code>callback</code> argument will be called\nautomatically with the arguments <code>premature</code>, <code>user_arg1</code>, <code>user_arg2</code>, etc.\n\nWhen success, returns a \"conditional true\" value (but not a <code>true</code>). Otherwise, returns a \"conditional false\" value and a string describing the error.\n\nThis API also respect the [[#lua_max_pending_timers|lua_max_pending_timers]] and [[#lua_max_running_timers|lua_max_running_timers]].\n\nThis API was first introduced in the <code>v0.10.9</code> release.\n\n== ngx.timer.running_count ==\n\n'''syntax:''' ''count = ngx.timer.running_count()''\n\n'''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*''\n\nReturns the number of timers currently running.\n\nThis directive was first introduced in the <code>v0.9.20</code> release.\n\n== ngx.timer.pending_count ==\n\n'''syntax:''' ''count = ngx.timer.pending_count()''\n\n'''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*''\n\nReturns the number of pending timers.\n\nThis directive was first introduced in the <code>v0.9.20</code> release.\n\n== ngx.config.subsystem ==\n\n'''syntax:''' ''subsystem = ngx.config.subsystem''\n\n'''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.*, init_by_lua*, init_worker_by_lua*, exit_worker_by_lua*''\n\nThis string field indicates the Nginx subsystem the current Lua environment is based on. For this module, this field always takes the string value <code>\"http\"</code>. For\n[https://github.com/openresty/stream-lua-nginx-module#readme ngx_stream_lua_module], however, this field takes the value <code>\"stream\"</code>.\n\nThis field was first introduced in the <code>0.10.1</code>.\n\n== ngx.config.debug ==\n\n'''syntax:''' ''debug = ngx.config.debug''\n\n'''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.*, init_by_lua*, init_worker_by_lua*, exit_worker_by_lua*''\n\nThis boolean field indicates whether the current Nginx is a debug build, i.e., being built by the <code>./configure</code> option <code>--with-debug</code>.\n\nThis field was first introduced in the <code>0.8.7</code>.\n\n== ngx.config.prefix ==\n\n'''syntax:''' ''prefix = ngx.config.prefix()''\n\n'''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.*, init_by_lua*, init_worker_by_lua*, exit_worker_by_lua*''\n\nReturns the Nginx server \"prefix\" path, as determined by the <code>-p</code> command-line option when running the Nginx executable, or the path specified by the <code>--prefix</code> command-line option when building Nginx with the <code>./configure</code> script.\n\nThis function was first introduced in the <code>0.9.2</code>.\n\n== ngx.config.nginx_version ==\n\n'''syntax:''' ''ver = ngx.config.nginx_version''\n\n'''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.*, init_by_lua*, init_worker_by_lua*, exit_worker_by_lua*''\n\nThis field take an integral value indicating the version number of the current Nginx core being used. For example, the version number <code>1.4.3</code> results in the Lua number 1004003.\n\nThis API was first introduced in the <code>0.9.3</code> release.\n\n== ngx.config.nginx_configure ==\n\n'''syntax:''' ''str = ngx.config.nginx_configure()''\n\n'''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.*, init_by_lua*''\n\nThis function returns a string for the Nginx <code>./configure</code> command's arguments string.\n\nThis API was first introduced in the <code>0.9.5</code> release.\n\n== ngx.config.ngx_lua_version ==\n\n'''syntax:''' ''ver = ngx.config.ngx_lua_version''\n\n'''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.*, init_by_lua*''\n\nThis field take an integral value indicating the version number of the current <code>ngx_lua</code> module being used. For example, the version number <code>0.9.3</code> results in the Lua number 9003.\n\nThis API was first introduced in the <code>0.9.3</code> release.\n\n== ngx.worker.exiting ==\n\n'''syntax:''' ''exiting = ngx.worker.exiting()''\n\n'''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.*, init_by_lua*, init_worker_by_lua*, exit_worker_by_lua*''\n\nThis function returns a boolean value indicating whether the current Nginx worker process already starts exiting. Nginx worker process exiting happens on Nginx server quit or configuration reload (aka HUP reload).\n\nThis API was first introduced in the <code>0.9.3</code> release.\n\n== ngx.worker.pid ==\n\n'''syntax:''' ''pid = ngx.worker.pid()''\n\n'''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.*, init_by_lua*, init_worker_by_lua*, exit_worker_by_lua*''\n\nThis function returns a Lua number for the process ID (PID) of the current Nginx worker process. This API is more efficient than <code>ngx.var.pid</code> and can be used in contexts where the [[#ngx.var.VARIABLE|ngx.var.VARIABLE]] API cannot be used (like [[#init_worker_by_lua|init_worker_by_lua]]).\n\nThis API was first introduced in the <code>0.9.5</code> release.\n\n== ngx.worker.pids ==\n\n'''syntax:''' ''pid = ngx.worker.pids()''\n\n'''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*''\n\nThis 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.\n== ngx.worker.count ==\n\n'''syntax:''' ''count = ngx.worker.count()''\n\n'''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.*, init_by_lua*, init_worker_by_lua*, exit_worker_by_lua*''\n\nReturns the total number of the Nginx worker processes (i.e., the value configured\nby the [https://nginx.org/en/docs/ngx_core_module.html#worker_processes worker_processes]\ndirective in <code>nginx.conf</code>).\n\nThis API was first introduced in the <code>0.9.20</code> release.\n\n== ngx.worker.id ==\n\n'''syntax:''' ''id = ngx.worker.id()''\n\n'''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.*, init_worker_by_lua*, exit_worker_by_lua*''\n\nReturns the ordinal number of the current Nginx worker processes (starting from number 0).\n\nSo if the total number of workers is <code>N</code>, then this method may return a number between 0\nand <code>N - 1</code> (inclusive).\n\nThis function returns meaningful values only for Nginx 1.9.1+. With earlier versions of Nginx, it\nalways returns <code>nil</code>.\n\nSee also [[#ngx.worker.count|ngx.worker.count]].\n\nThis API was first introduced in the <code>0.9.20</code> release.\n\n== ngx.semaphore ==\n\n'''syntax:''' ''local semaphore = require \"ngx.semaphore\"''\n\nThis is a Lua module that implements a classic-style semaphore API for efficient synchronizations among\ndifferent \"light threads\". Sharing the same semaphore among different \"light threads\" created in different (request)\ncontexts are also supported as long as the \"light threads\" reside in the same Nginx worker process\nand the [[#lua_code_cache|lua_code_cache]] directive is turned on (which is the default).\n\nThis Lua module does not ship with this ngx_lua module itself rather it is shipped with\nthe\n[https://github.com/openresty/lua-resty-core lua-resty-core] library.\n\nPlease refer to the [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/semaphore.md documentation]\nfor this <code>ngx.semaphore</code> Lua module in [https://github.com/openresty/lua-resty-core lua-resty-core]\nfor more details.\n\nThis feature requires at least ngx_lua <code>v0.10.0</code>.\n\n== ngx.balancer ==\n\n'''syntax:''' ''local balancer = require \"ngx.balancer\"''\n\nThis is a Lua module that provides a Lua API to allow defining completely dynamic load balancers\nin pure Lua.\n\nThis Lua module does not ship with this ngx_lua module itself rather it is shipped with\nthe\n[https://github.com/openresty/lua-resty-core lua-resty-core] library.\n\nPlease refer to the [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/balancer.md documentation]\nfor this <code>ngx.balancer</code> Lua module in [https://github.com/openresty/lua-resty-core lua-resty-core]\nfor more details.\n\nThis feature requires at least ngx_lua <code>v0.10.0</code>.\n\n== ngx.ssl ==\n\n'''syntax:''' ''local ssl = require \"ngx.ssl\"''\n\nThis Lua module provides API functions to control the SSL handshake process in contexts like\n[ssl_certificate_by_lua*](#ssl_certificate_by_lua_block).\n\nThis Lua module does not ship with this ngx_lua module itself rather it is shipped with\nthe\n[https://github.com/openresty/lua-resty-core lua-resty-core] library.\n\nPlease refer to the [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md documentation]\nfor this <code>ngx.ssl</code> Lua module for more details.\n\nThis feature requires at least ngx_lua <code>v0.10.0</code>.\n\n== ngx.ocsp ==\n\n'''syntax:''' ''local ocsp = require \"ngx.ocsp\"''\n\nThis Lua module provides API to perform OCSP queries, OCSP response validations, and\nOCSP stapling planting.\n\nUsually, this module is used together with the [ngx.ssl](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md)\nmodule in the\ncontext of [ssl_certificate_by_lua*](#ssl_certificate_by_lua_block).\n\nThis Lua module does not ship with this ngx_lua module itself rather it is shipped with\nthe\n[https://github.com/openresty/lua-resty-core lua-resty-core] library.\n\nPlease refer to the [https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ocsp.md documentation]\nfor this <code>ngx.ocsp</code> Lua module for more details.\n\nThis feature requires at least ngx_lua <code>v0.10.0</code>.\n\n== ndk.set_var.DIRECTIVE ==\n\n'''syntax:''' ''res = ndk.set_var.DIRECTIVE_NAME''\n\n'''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*''\n\nThis 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 <code>ndk_set_var_value</code>.\n\nFor example, the following [[HttpSetMiscModule]] directives can be invoked this way:\n\n* [[HttpSetMiscModule#set_quote_sql_str|set_quote_sql_str]]\n* [[HttpSetMiscModule#set_quote_pgsql_str|set_quote_pgsql_str]]\n* [[HttpSetMiscModule#set_quote_json_str|set_quote_json_str]]\n* [[HttpSetMiscModule#set_unescape_uri|set_unescape_uri]]\n* [[HttpSetMiscModule#set_escape_uri|set_escape_uri]]\n* [[HttpSetMiscModule#set_encode_base32|set_encode_base32]]\n* [[HttpSetMiscModule#set_decode_base32|set_decode_base32]]\n* [[HttpSetMiscModule#set_encode_base64|set_encode_base64]]\n* [[HttpSetMiscModule#set_decode_base64|set_decode_base64]]\n* [[HttpSetMiscModule#set_encode_base64|set_encode_hex]]\n* [[HttpSetMiscModule#set_decode_base64|set_decode_hex]]\n* [[HttpSetMiscModule#set_encode_base64|set_sha1]]\n* [[HttpSetMiscModule#set_decode_base64|set_md5]]\n\nFor instance,\n\n<geshi lang=\"lua\">\n    local res = ndk.set_var.set_escape_uri('a/b')\n    -- now res == 'a%2fb'\n</geshi>\n\nSimilarly, the following directives provided by [[HttpEncryptedSessionModule]] can be invoked from within Lua too:\n\n* [[HttpEncryptedSessionModule#set_encrypt_session|set_encrypt_session]]\n* [[HttpEncryptedSessionModule#set_decrypt_session|set_decrypt_session]]\n\nThis feature requires the [https://github.com/simplresty/ngx_devel_kit ngx_devel_kit] module.\n\n== coroutine.create ==\n\n'''syntax:''' ''co = coroutine.create(f)''\n\n'''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*''\n\nCreates a user Lua coroutines with a Lua function, and returns a coroutine object.\n\nSimilar to the standard Lua [https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.create coroutine.create] API, but works in the context of the Lua coroutines created by ngx_lua.\n\nThis API was first usable in the context of [[#init_by_lua|init_by_lua*]] since the <code>0.9.2</code>.\n\nThis API was first introduced in the <code>v0.6.0</code> release.\n\n== coroutine.resume ==\n\n'''syntax:''' ''ok, ... = coroutine.resume(co, ...)''\n\n'''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*''\n\nResumes the execution of a user Lua coroutine object previously yielded or just created.\n\nSimilar 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.\n\nThis API was first usable in the context of [[#init_by_lua|init_by_lua*]] since the <code>0.9.2</code>.\n\nThis API was first introduced in the <code>v0.6.0</code> release.\n\n== coroutine.yield ==\n\n'''syntax:''' ''... = coroutine.yield(...)''\n\n'''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*''\n\nYields the execution of the current user Lua coroutine.\n\nSimilar to the standard Lua [https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.yield coroutine.yield] API, but works in the context of the Lua coroutines created by ngx_lua.\n\nThis API was first usable in the context of [[#init_by_lua|init_by_lua*]] since the <code>0.9.2</code>.\n\nThis API was first introduced in the <code>v0.6.0</code> release.\n\n== coroutine.wrap ==\n\n'''syntax:''' ''co = coroutine.wrap(f)''\n\n'''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*''\n\nSimilar 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.\n\nThis API was first usable in the context of [[#init_by_lua|init_by_lua*]] since the <code>0.9.2</code>.\n\nThis API was first introduced in the <code>v0.6.0</code> release.\n\n== coroutine.running ==\n\n'''syntax:''' ''co = coroutine.running()''\n\n'''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*''\n\nIdentical to the standard Lua [https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.running coroutine.running] API.\n\nThis API was first usable in the context of [[#init_by_lua|init_by_lua*]] since the <code>0.9.2</code>.\n\nThis API was first enabled in the <code>v0.6.0</code> release.\n\n== coroutine.status ==\n\n'''syntax:''' ''status = coroutine.status(co)''\n\n'''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*''\n\nIdentical to the standard Lua [https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.status coroutine.status] API.\n\nThis API was first usable in the context of [[#init_by_lua|init_by_lua*]] since the <code>0.9.2</code>.\n\nThis API was first enabled in the <code>v0.6.0</code> release.\n\n== ngx.run_worker_thread ==\n\n'''syntax:''' ''ok, res1, res2, ... = ngx.run_worker_thread(threadpool, module_name, func_name, arg1, arg2, ...)''\n\n'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*''\n\n'''This API is still experimental and may change in the future without notice.'''\n\n'''This API is available only for Linux.'''\n\nWrap 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.\n\nOnly the following ngx_lua APIs could be used in `function_name` function of the `module` module:\n\n* `ngx.encode_base64`\n* `ngx.decode_base64`\n\n* `ngx.hmac_sha1`\n* `ngx.encode_args`\n* `ngx.decode_args`\n* `ngx.quote_sql_str`\n\n* `ngx.re.match`\n* `ngx.re.find`\n* `ngx.re.gmatch`\n* `ngx.re.sub`\n* `ngx.re.gsub`\n\n* `ngx.crc32_short`\n* `ngx.crc32_long`\n* `ngx.hmac_sha1`\n* `ngx.md5_bin`\n* `ngx.md5`\n\n* `ngx.config.subsystem`\n* `ngx.config.debug`\n* `ngx.config.prefix`\n* `ngx.config.nginx_version`\n* `ngx.config.nginx_configure`\n* `ngx.config.ngx_lua_version`\n\n\nThe first argument `threadpool` specifies the Nginx thread pool name defined by [thread_pool](https://nginx.org/en/docs/ngx_core_module.html#thread_pool).\n\nThe second argument <code>module_name</code> 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.\n\n<geshi lang=\"nginx\">\nlua_package_path '/opt/openresty/?.lua;;';\n</geshi>\n\nThe third argument <code>func_name</code> specifies the function field in the module table as the second argument.\n\nThe type of <code>arg</code>s must be one of type below:\n\n* boolean\n* number\n* string\n* nil\n* table (the table may be recursive, and contains members of types above.)\n\nThe <code>ok</code> is in boolean type, which indicate the C land error (failed to get thread from thread pool, pcall the module function failed, .etc). If <code>ok</code> is <code>false</code>, the <code>res1</code> is the error string.\n\nThe return values (res1, ...) are returned by invocation of the module function. Normally, the <code>res1</code> should be in boolean type, so that the caller could inspect the error.\n\nThis API is useful when you need to execute the below types of tasks:\n\n* CPU bound task, e.g. do md5 calculation\n* File I/O task\n* Call <code>os.execute()</code> or blocking C API via <code>ffi</code>\n* Call external Lua library not based on cosocket or nginx\n\nExample1: do md5 calculation.\n\n<geshi lang=\"nginx\">\nlocation /calc_md5 {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, md5_or_err = ngx.run_worker_thread(\"testpool\", \"md5\", \"md5\")\n        ngx.say(ok, \" : \", md5_or_err)\n    }\n}\n\n</geshi>\n\n<code>md5.lua</code>\n\n<geshi lang=\"lua\">\nlocal function md5()\n    return ngx.md5(\"hello\")\nend\nreturn {md5=md5}\n</geshi>\n\nExample2: write logs into the log file.\n\n<geshi lang=\"nginx\">\nlocation /write_log_file {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, err = ngx.run_worker_thread(\"testpool\", \"write_log_file\", \"log\", ngx.var.arg_str)\n        if not ok then\n            ngx.say(ok, \" : \", err)\n            return\n        end\n        ngx.say(ok)\n    }\n}\n</geshi>\n\n<code>write_log_file.lua</code>\n\n<geshi lang=\"lua\">\nlocal function log(str)\n    local file, err = io.open(\"/tmp/tmp.log\", \"a\")\n    if not file then\n        return false, err\n    end\n    file:write(str)\n    file:flush()\n    file:close()\n    return true\nend\nreturn {log=log}\n</geshi>\n\n= Obsolete Sections =\n\nThis section is just holding obsolete documentation sections that have been either renamed or removed so that existing links over the web are still valid.\n\n== Special PCRE Sequences ==\n\nThis section has been renamed to [[#Special Escaping Sequences|Special Escaping Sequences]].\n\n== Lua/LuaJIT bytecode support ==\n\nThis section has been renamed to\n[[#LuaJIT bytecode support|LuaJIT bytecode support]]. As of version\n<code>v0.10.16</code> of this module, the standard Lua interpreter (also known\nas \"PUC-Rio Lua\") is not supported anymore.\n"
  },
  {
    "path": "dtrace/ngx_lua_provider.d",
    "content": "provider nginx_lua {\n    probe http__lua__info(char *s);\n\n    /* lua_State *L */\n    probe http__lua__register__preload__package(void *L, u_char *pkg);\n\n    probe http__lua__req__socket__consume__preread(void *r,\n            u_char *data, size_t len);\n\n    /* lua_State *parent, lua_State *child */\n    probe http__lua__user__coroutine__create(void *r,\n            void *parent, void *child);\n\n    /* lua_State *parent, lua_State *child */\n    probe http__lua__user__coroutine__resume(void *r,\n                                             void *parent, void *child);\n\n    /* lua_State *parent, lua_State *child */\n    probe http__lua__user__coroutine__yield(void *r,\n                                            void *parent, void *child);\n\n    /* lua_State *L */\n    probe http__lua__thread__yield(void *r, void *L);\n\n    /* ngx_http_lua_socket_tcp_upstream_t *u */\n    probe http__lua__socket__tcp__send__start(void *r,\n            void *u, u_char *data, size_t len);\n\n    /* ngx_http_lua_socket_tcp_upstream_t *u */\n    probe http__lua__socket__tcp__receive__done(void *r,\n            void *u, u_char *data, size_t len);\n\n    /* ngx_http_lua_socket_tcp_upstream_t *u */\n    probe http__lua__socket__tcp__setkeepalive__buf__unread(\n            void *r, void *u, u_char *data, size_t len);\n\n    /* lua_State *creator, lua_State *newthread */\n    probe http__lua__user__thread__spawn(void *r,\n            void *creator, void *newthread);\n\n    /* lua_State *thread, ngx_http_lua_ctx_t *ctx */\n    probe http__lua__thread__delete(void *r, void *thread, void *ctx);\n\n    /* lua_State *thread */\n    probe http__lua__run__posted__thread(void *r, void *thread,\n            int status);\n\n    probe http__lua__coroutine__done(void *r, void *co,\n            int success);\n\n    /* lua_State *parent, lua_State *child */\n    probe http__lua__user__thread__wait(void *parent, void *child);\n};\n\n\n#pragma D attributes Evolving/Evolving/Common      provider nginx_lua provider\n#pragma D attributes Private/Private/Unknown       provider nginx_lua module\n#pragma D attributes Private/Private/Unknown       provider nginx_lua function\n#pragma D attributes Private/Private/Common        provider nginx_lua name\n#pragma D attributes Evolving/Evolving/Common      provider nginx_lua args\n\n"
  },
  {
    "path": "misc/recv-until-pm/lib/RecvUntil.pm",
    "content": "package RecvUntil;\n\nuse strict;\nuse warnings;\n\nsub recv_until {\n    my ($pat) = @_;\n\n    my $len = length $pat;\n    my @backtracks;\n\n    for (my $i = 1; $i <= $len - 1; $i++) {\n        my $matched_prefix_len = 1;\n        while ($matched_prefix_len <= $len - $i - 1) {\n            #while (1) {\n            #my $left = $len - $i;\n            #warn \"left: $i: $len: \", $len - 1 - $i, \"\\n\";\n            #warn \"matched_prefix_len: $matched_prefix_len\\n\";\n\n            #while (1) {\n            my $prefix = substr($pat, 0, $matched_prefix_len);\n            my $next = substr($pat, $matched_prefix_len, 1);\n\n            my $prefix2 = substr($pat, $i, $matched_prefix_len);\n            my $next2 = substr($pat, $i + $matched_prefix_len, 1);\n\n            #warn \"$i: global prefix $prefix $next\\n\";\n            #warn \"$i: local prefix $prefix2 $next2\\n\";\n\n            if ($prefix2 eq $prefix) {\n                if ($next2 eq $next) {\n                    $matched_prefix_len++;\n                    next;\n                }\n\n                #warn \"$matched_prefix_len: $prefix: found match at $i (next $next, next2 $next2)\\n\";\n                my $cur_state = $i + $matched_prefix_len;\n                my $new_state = $matched_prefix_len + 1;\n\n                my $matched = substr($pat, 0, $cur_state);\n\n                my $chain = $backtracks[$cur_state - 2];\n                if (!$chain) {\n                    $chain = [];\n                    $backtracks[$cur_state - 2] = $chain;\n                }\n\n                my $found = 0;\n                for my $rec (@$chain) {\n                    if ($rec->{char} eq $next) {\n                        $found = 1;\n\n                        if ($rec->{new_state} < $new_state) {\n                            warn \"overriding...\\n\";\n                            $rec->{new_state} = $new_state;\n                        }\n                    }\n                }\n\n                if (!$found) {\n                    warn \"on state $cur_state ($matched), if next is '$next', \",\n                        \"then backtrack to state $new_state ($prefix$next)\\n\";\n\n                    push @$chain, { char => $next, new_state => $new_state };\n                }\n\n                #if ($matched_prefix_len > 1) {\n                #$i += $matched_prefix_len - 1;\n                #}\n\n                last;\n            }\n\n            last;\n        }\n    }\n\n    return sub {\n        my ($txt) = @_;\n\n        my $max_state = length $pat;\n        my $len = length $txt;\n        my $state = 0;\n        my $ret = '';\n\n        for (my $i = 0; $i < $len; $i++) {\n            # read the char\n            my $c = substr($txt, $i, 1);\n\n            #warn \"$state: read char at $i: $c\\n\";\n            #warn \"matched: $ret\\n\";\n\n            my $expected = substr($pat, $state, 1);\n            if ($expected eq $c) {\n                #warn \"matched the char in pattern.\\n\";\n                $state++;\n\n                if ($state == $max_state) {\n                    last;\n                }\n\n                next;\n            }\n\n            if ($state == 0) {\n                #warn \"did not match the first char in pattern\\n\";\n                $ret .= $c;\n                next;\n            }\n\n            my $old_state;\n            my $matched;\n            my $chain = $backtracks[$state - 2];\n            for my $rec (@$chain) {\n                if ($rec->{char} eq $c) {\n                    $old_state = $state;\n                    $state = $rec->{new_state};\n                    #warn \"matched the char for backtracking to state $state\\n\";\n                    $matched = 1;\n                    last;\n                }\n            }\n\n            if (!$matched) {\n                $ret .= substr($pat, 0, $state);\n                $state = 0;\n                redo;\n            }\n\n            $ret .= substr($pat, 0, $old_state + 1 - $state);\n            next;\n        }\n\n        return $ret;\n    };\n}\n\n1;\n"
  },
  {
    "path": "misc/recv-until-pm/t/sanity.t",
    "content": "# vi:ft=\n\nuse 5.10.1;\nuse Test::Base;\nuse RecvUntil;\n\nplan tests => 1 * blocks();\n\nrun {\n    my $block = shift;\n    my $name = $block->name;\n    my $pat = $block->pat // die \"$name: No --- pat found\";\n    my $txt = $block->txt // die \"$name: No --- txt found\";\n\n    my $expected = $block->out // die \"$name: No --- out found\";\n\n    my $it = RecvUntil::recv_until($pat);\n    is $it->($txt), $expected, \"$name: output ok\";\n};\n\n__DATA__\n\n=== TEST 1:\n--- pat: abcabd\n--- txt: abcabcabd\n--- out: abc\n\n\n\n=== TEST 2:\n--- pat: aa\n--- txt: abcabcaad\n--- out: abcabc\n\n\n\n=== TEST 3:\n--- pat: ab\n--- txt: bbcabcaad\n--- out: bbc\n\n\n\n=== TEST 4:\n--- pat: aaa\n--- txt: abaabcaaaef\n--- out: abaabc\n\n\n\n=== TEST 5:\n--- pat: aaaaad\n--- txt: baaaaaaaaeaaaaaaadf\n--- out: baaaaaaaaeaa\n\n\n\n=== TEST 6:\n--- pat: abacadae\n--- txt: a\n--- out:\n\n\n\n=== TEST 7:\n--- pat: abacadae\n--- txt: ababacadae\n--- out: ab\n\n\n\n=== TEST 8:\n--- pat: abacadae\n--- txt: abacabacadae\n--- out: abac\n\n\n\n=== TEST 9:\n--- pat: abacadae\n--- txt: abaabacadae\n--- out: aba\n\n\n\n=== TEST 10:\n--- pat: abacadae\n--- txt: abacadabacadae\n--- out: abacad\n\n\n\n=== TEST 11:\n--- pat: abcabdabcabe\n--- txt: abcabdabcabdabcabe\n--- out: abcabd\n\n\n\n=== TEST 12:\n--- pat: abcabdabcabe\n--- txt: abcabdabcabcabdabcabe\n--- out: abcabdabc\n\n\n\n=== TEST 13:\n--- pat: abcabdabcabe\n--- txt: abcabcabdabcabe\n--- out: abc\n\n\n\n=== TEST 14:\n--- pat: abcabdabcabe\n--- txt: ababcabdabcabe\n--- out: ab\n\n\n\n=== TEST 15:\n--- pat: abcdef\n--- txt: abcabcdef\n--- out: abc\n\n\n\n=== TEST 16:\n--- pat: -- abc\n--- txt: ---- abc\n--- out: --\n\n\n\n=== TEST 17:\n--- pat: yz--ababyz\n--- txt: \n--- out: --\n--- SKIP\n\n"
  },
  {
    "path": "src/api/ngx_http_lua_api.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_API_H_INCLUDED_\n#define _NGX_HTTP_LUA_API_H_INCLUDED_\n\n\n#include <nginx.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#include <lua.h>\n#include <stdint.h>\n\n\n/* Public API for other Nginx modules */\n\n\n#define ngx_http_lua_version  10030\n#define NGX_HTTP_LUA_EXPORT_CO_CTX_CLEANUP 1\n\n\ntypedef struct ngx_http_lua_co_ctx_s  ngx_http_lua_co_ctx_t;\n\n\ntypedef struct {\n    uint8_t         type;\n\n    union {\n        int         b; /* boolean */\n        lua_Number  n; /* number */\n        ngx_str_t   s; /* string */\n    } value;\n\n} ngx_http_lua_value_t;\n\n\ntypedef struct {\n    int          len;\n    /* this padding hole on 64-bit systems is expected */\n    u_char      *data;\n} ngx_http_lua_ffi_str_t;\n\n\nlua_State *ngx_http_lua_get_global_state(ngx_conf_t *cf);\n\nngx_http_request_t *ngx_http_lua_get_request(lua_State *L);\n\nngx_int_t ngx_http_lua_add_package_preload(ngx_conf_t *cf, const char *package,\n    lua_CFunction func);\n\nngx_int_t ngx_http_lua_shared_dict_get(ngx_shm_zone_t *shm_zone,\n    u_char *key_data, size_t key_len, ngx_http_lua_value_t *value);\n\nngx_shm_zone_t *ngx_http_lua_find_zone(u_char *name_data, size_t name_len);\n\nngx_shm_zone_t *ngx_http_lua_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name,\n    size_t size, void *tag);\n\nngx_http_lua_co_ctx_t *ngx_http_lua_get_cur_co_ctx(ngx_http_request_t *r);\n\nvoid ngx_http_lua_set_cur_co_ctx(ngx_http_request_t *r,\n    ngx_http_lua_co_ctx_t *coctx);\n\nlua_State *ngx_http_lua_get_co_ctx_vm(ngx_http_lua_co_ctx_t *coctx);\n\n\nvoid *ngx_http_lua_get_co_ctx_data(ngx_http_lua_co_ctx_t *coctx);\nvoid ngx_http_lua_set_co_ctx_cleanup(ngx_http_lua_co_ctx_t *coctx,\n    ngx_http_cleanup_pt cleanup, void *data);\nvoid ngx_http_lua_cleanup_co_ctx_pending_operation(\n    ngx_http_lua_co_ctx_t *coctx);\n\nvoid ngx_http_lua_co_ctx_resume_helper(ngx_http_lua_co_ctx_t *coctx, int nrets);\n\nint ngx_http_lua_get_lua_http10_buffering(ngx_http_request_t *r);\n\n\n#endif /* _NGX_HTTP_LUA_API_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ddebug.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _DDEBUG_H_INCLUDED_\n#define _DDEBUG_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <nginx.h>\n#include <ngx_core.h>\n\n\n#if defined(DDEBUG) && (DDEBUG)\n\n#   if (NGX_HAVE_VARIADIC_MACROS)\n\n#       define dd(...) fprintf(stderr, \"lua *** %s: \", __func__);            \\\n            fprintf(stderr, __VA_ARGS__);                                    \\\n            fprintf(stderr, \" at %s line %d.\\n\", __FILE__, __LINE__)\n\n#   else\n\n#include <stdarg.h>\n#include <stdio.h>\n\n#include <stdarg.h>\n\nstatic ngx_inline void\ndd(const char *fmt, ...) {\n}\n\n#    endif\n\n#else\n\n#   if (NGX_HAVE_VARIADIC_MACROS)\n\n#       define dd(...)\n\n#   else\n\n#include <stdarg.h>\n\nstatic ngx_inline void\ndd(const char *fmt, ...) {\n}\n\n#   endif\n\n#endif\n\n#if defined(DDEBUG) && (DDEBUG)\n\n#define dd_check_read_event_handler(r)                                       \\\n    dd(\"r->read_event_handler = %s\",                                         \\\n        r->read_event_handler == ngx_http_block_reading ?                    \\\n            \"ngx_http_block_reading\" :                                       \\\n        r->read_event_handler == ngx_http_test_reading ?                     \\\n            \"ngx_http_test_reading\" :                                        \\\n        r->read_event_handler == ngx_http_request_empty_handler ?            \\\n            \"ngx_http_request_empty_handler\" : \"UNKNOWN\")\n\n#define dd_check_write_event_handler(r)                                      \\\n    dd(\"r->write_event_handler = %s\",                                        \\\n        r->write_event_handler == ngx_http_handler ?                         \\\n            \"ngx_http_handler\" :                                             \\\n        r->write_event_handler == ngx_http_core_run_phases ?                 \\\n            \"ngx_http_core_run_phases\" :                                     \\\n        r->write_event_handler == ngx_http_request_empty_handler ?           \\\n            \"ngx_http_request_empty_handler\" : \"UNKNOWN\")\n\n#else\n\n#define dd_check_read_event_handler(r)\n#define dd_check_write_event_handler(r)\n\n#endif\n\n\n#endif /* _DDEBUG_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_accessby.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include <nginx.h>\n#include \"ngx_http_lua_accessby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_exception.h\"\n#include \"ngx_http_lua_cache.h\"\n\n\nstatic ngx_int_t ngx_http_lua_access_by_chunk(lua_State *L,\n    ngx_http_request_t *r);\n\n\nngx_int_t\nngx_http_lua_access_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                   rc;\n    ngx_http_lua_ctx_t         *ctx;\n    ngx_http_lua_loc_conf_t    *llcf;\n    ngx_http_lua_main_conf_t   *lmcf;\n    ngx_http_phase_handler_t    tmp, *ph, *cur_ph, *last_ph;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua access handler, uri:\\\"%V\\\" c:%ud\", &r->uri,\n                   r->main->count);\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    if (!lmcf->postponed_to_access_phase_end) {\n\n        lmcf->postponed_to_access_phase_end = 1;\n\n        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n        ph = cmcf->phase_engine.handlers;\n        cur_ph = &ph[r->phase_handler];\n\n        /* we should skip the post_access phase handler here too */\n        last_ph = &ph[cur_ph->next - 2];\n\n        dd(\"ph cur: %d, ph next: %d\", (int) r->phase_handler,\n           (int) (cur_ph->next - 2));\n\n#if 0\n        if (cur_ph == last_ph) {\n            dd(\"XXX our handler is already the last access phase handler\");\n        }\n#endif\n\n        if (cur_ph < last_ph) {\n            dd(\"swapping the contents of cur_ph and last_ph...\");\n\n            tmp = *cur_ph;\n\n            memmove(cur_ph, cur_ph + 1,\n                    (last_ph - cur_ph) * sizeof (ngx_http_phase_handler_t));\n\n            *last_ph = tmp;\n\n            r->phase_handler--; /* redo the current ph */\n\n            return NGX_DECLINED;\n        }\n    }\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->access_handler == NULL) {\n        dd(\"no access handler found\");\n        return NGX_DECLINED;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    dd(\"ctx = %p\", ctx);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    dd(\"entered? %d\", (int) ctx->entered_access_phase);\n\n\n    if (ctx->entered_access_phase) {\n        dd(\"calling wev handler\");\n        rc = ctx->resume_handler(r);\n        dd(\"wev handler returns %d\", (int) rc);\n\n        if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) {\n            return rc;\n        }\n\n        if (rc == NGX_OK) {\n            if (r->header_sent\n                || (r->headers_out.status != 0 && ctx->out != NULL))\n            {\n                dd(\"header already sent\");\n\n                /* response header was already generated in access_by_lua*,\n                 * so it is no longer safe to proceed to later phases\n                 * which may generate responses again */\n\n                if (!ctx->eof) {\n                    dd(\"eof not yet sent\");\n\n                    rc = ngx_http_lua_send_chain_link(r, ctx, NULL\n                                                     /* indicate last_buf */);\n                    if (rc == NGX_ERROR || rc > NGX_OK) {\n                        return rc;\n                    }\n                }\n\n                return NGX_HTTP_OK;\n            }\n\n            return NGX_OK;\n        }\n\n        return NGX_DECLINED;\n    }\n\n    if (ctx->waiting_more_body) {\n        dd(\"WAITING MORE BODY\");\n        return NGX_DONE;\n    }\n\n    if (llcf->force_read_body && !ctx->read_body_done) {\n        r->request_body_in_single_buf = 1;\n        r->request_body_in_persistent_file = 1;\n        r->request_body_in_clean_file = 1;\n\n        rc = ngx_http_read_client_request_body(r,\n                                       ngx_http_lua_generic_phase_post_read);\n\n        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n\n        if (rc == NGX_AGAIN) {\n            ctx->waiting_more_body = 1;\n            return NGX_DONE;\n        }\n    }\n\n    dd(\"calling access handler\");\n    return llcf->access_handler(r);\n}\n\n\nngx_int_t\nngx_http_lua_access_handler_inline(ngx_http_request_t *r)\n{\n    ngx_int_t                  rc;\n    lua_State                 *L;\n    ngx_http_lua_loc_conf_t   *llcf;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->access_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua inline script (w/ cache) sp = 1 */\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       llcf->access_src.value.data,\n                                       llcf->access_src.value.len,\n                                       &llcf->access_src_ref,\n                                       llcf->access_src_key,\n                                       (const char *) llcf->access_chunkname);\n\n    if (rc != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    return ngx_http_lua_access_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_access_handler_file(ngx_http_request_t *r)\n{\n    u_char                    *script_path;\n    ngx_int_t                  rc;\n    ngx_str_t                  eval_src;\n    lua_State                 *L;\n    ngx_http_lua_loc_conf_t   *llcf;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    /* Eval nginx variables in code path string first */\n    if (ngx_http_complex_value(r, &llcf->access_src, &eval_src) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,\n                                           eval_src.len);\n\n    if (script_path == NULL) {\n        return NGX_ERROR;\n    }\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->access_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua script file (w/ cache)        sp = 1 */\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,\n                                     &llcf->access_src_ref,\n                                     llcf->access_src_key);\n    if (rc != NGX_OK) {\n        if (rc < NGX_HTTP_SPECIAL_RESPONSE) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_access_by_chunk(L, r);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_access_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    int                  co_ref;\n    ngx_int_t            rc;\n    ngx_uint_t           nreqs;\n    lua_State           *co;\n    ngx_event_t         *rev;\n    ngx_connection_t    *c;\n    ngx_http_lua_ctx_t  *ctx;\n    ngx_pool_cleanup_t  *cln;\n\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    /*  {{{ new coroutine to handle request */\n    co = ngx_http_lua_new_thread(r, L, &co_ref);\n\n    if (co == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"lua: failed to create new coroutine \"\n                      \"to handle request\");\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    /*  move code closure to new coroutine */\n    lua_xmove(L, co, 1);\n\n#ifndef OPENRESTY_LUAJIT\n    /*  set closure's env table to new coroutine's globals table */\n    ngx_http_lua_get_globals_table(co);\n    lua_setfenv(co, -2);\n#endif\n\n    /*  save nginx request in coroutine globals table */\n    ngx_http_lua_set_req(co, r);\n\n    /*  {{{ initialize request context */\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    dd(\"ctx = %p\", ctx);\n\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_lua_reset_ctx(r, L, ctx);\n\n    ctx->entered_access_phase = 1;\n\n    ctx->cur_co_ctx = &ctx->entry_co_ctx;\n    ctx->cur_co_ctx->co = co;\n    ctx->cur_co_ctx->co_ref = co_ref;\n#ifdef NGX_LUA_USE_ASSERT\n    ctx->cur_co_ctx->co_top = 1;\n#endif\n\n    ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);\n\n    /*  }}} */\n\n    /*  {{{ register nginx pool cleanup hooks */\n    if (ctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n        if (cln == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        cln->handler = ngx_http_lua_request_cleanup_handler;\n        cln->data = ctx;\n        ctx->cleanup = &cln->handler;\n    }\n    /*  }}} */\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_ACCESS;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->check_client_abort) {\n        r->read_event_handler = ngx_http_lua_rd_check_broken_connection;\n\n#if (NGX_HTTP_V2)\n        if (!r->stream) {\n#endif\n\n        rev = r->connection->read;\n\n        if (!rev->active) {\n            if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n\n#if (NGX_HTTP_V2)\n        }\n#endif\n\n    } else {\n        r->read_event_handler = ngx_http_block_reading;\n    }\n\n    c = r->connection;\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(L, r, ctx, 0);\n\n    dd(\"returned %d\", (int) rc);\n\n    if (rc == NGX_ERROR || rc > NGX_OK) {\n        return rc;\n    }\n\n    if (rc == NGX_AGAIN) {\n        rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);\n\n        if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) {\n            return rc;\n        }\n\n        if (rc != NGX_OK) {\n            return NGX_DECLINED;\n        }\n\n    } else if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n\n        rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);\n\n        if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) {\n            return rc;\n        }\n\n        if (rc != NGX_OK) {\n            return NGX_DECLINED;\n        }\n    }\n\n#if 1\n    if (rc == NGX_OK) {\n        if (r->header_sent || (r->headers_out.status != 0 && ctx->out != NULL))\n        {\n            dd(\"header already sent\");\n\n            /* response header was already generated in access_by_lua*,\n             * so it is no longer safe to proceed to later phases\n             * which may generate responses again */\n\n            if (!ctx->eof) {\n                dd(\"eof not yet sent\");\n\n                rc = ngx_http_lua_send_chain_link(r, ctx, NULL\n                                                  /* indicate last_buf */);\n                if (rc == NGX_ERROR || rc > NGX_OK) {\n                    return rc;\n                }\n            }\n\n            return NGX_HTTP_OK;\n        }\n\n        return NGX_OK;\n    }\n#endif\n\n    return NGX_DECLINED;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_accessby.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_ACCESSBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_ACCESSBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_access_handler(ngx_http_request_t *r);\nngx_int_t ngx_http_lua_access_handler_inline(ngx_http_request_t *r);\nngx_int_t ngx_http_lua_access_handler_file(ngx_http_request_t *r);\n\n\n#endif /* _NGX_HTTP_LUA_ACCESSBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_api.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_common.h\"\n#include \"api/ngx_http_lua_api.h\"\n#include \"ngx_http_lua_shdict.h\"\n#include \"ngx_http_lua_util.h\"\n\n\nlua_State *\nngx_http_lua_get_global_state(ngx_conf_t *cf)\n{\n    ngx_http_lua_main_conf_t *lmcf;\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n\n    return lmcf->lua;\n}\n\n\nngx_http_request_t *\nngx_http_lua_get_request(lua_State *L)\n{\n    return ngx_http_lua_get_req(L);\n}\n\n\nstatic ngx_int_t ngx_http_lua_shared_memory_init(ngx_shm_zone_t *shm_zone,\n    void *data);\n\n\nngx_int_t\nngx_http_lua_add_package_preload(ngx_conf_t *cf, const char *package,\n    lua_CFunction func)\n{\n    lua_State                     *L;\n    ngx_http_lua_main_conf_t      *lmcf;\n    ngx_http_lua_preload_hook_t   *hook;\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n\n    L = lmcf->lua;\n\n    if (L) {\n        lua_getglobal(L, \"package\");\n        lua_getfield(L, -1, \"preload\");\n        lua_pushcfunction(L, func);\n        lua_setfield(L, -2, package);\n        lua_pop(L, 2);\n    }\n\n    /* we always register preload_hooks since we always create new Lua VMs\n     * when lua code cache is off. */\n\n    if (lmcf->preload_hooks == NULL) {\n        lmcf->preload_hooks =\n            ngx_array_create(cf->pool, 4,\n                             sizeof(ngx_http_lua_preload_hook_t));\n\n        if (lmcf->preload_hooks == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    hook = ngx_array_push(lmcf->preload_hooks);\n    if (hook == NULL) {\n        return NGX_ERROR;\n    }\n\n    hook->package = (u_char *) package;\n    hook->loader = func;\n\n    return NGX_OK;\n}\n\n\nngx_shm_zone_t *\nngx_http_lua_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size,\n    void *tag)\n{\n    ngx_http_lua_main_conf_t     *lmcf;\n    ngx_shm_zone_t              **zp;\n    ngx_shm_zone_t               *zone;\n    ngx_http_lua_shm_zone_ctx_t  *ctx;\n    ngx_int_t                     n;\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n    if (lmcf == NULL) {\n        return NULL;\n    }\n\n    if (lmcf->shm_zones == NULL) {\n        lmcf->shm_zones = ngx_palloc(cf->pool, sizeof(ngx_array_t));\n        if (lmcf->shm_zones == NULL) {\n            return NULL;\n        }\n\n        if (ngx_array_init(lmcf->shm_zones, cf->pool, 2,\n                           sizeof(ngx_shm_zone_t *))\n            != NGX_OK)\n        {\n            return NULL;\n        }\n    }\n\n    zone = ngx_shared_memory_add(cf, name, (size_t) size, tag);\n    if (zone == NULL) {\n        return NULL;\n    }\n\n    if (zone->data) {\n        ctx = (ngx_http_lua_shm_zone_ctx_t *) zone->data;\n        return &ctx->zone;\n    }\n\n    n = sizeof(ngx_http_lua_shm_zone_ctx_t);\n\n    ctx = ngx_pcalloc(cf->pool, n);\n    if (ctx == NULL) {\n        return NULL;\n    }\n\n    ctx->lmcf = lmcf;\n    ctx->log = &cf->cycle->new_log;\n    ctx->cycle = cf->cycle;\n\n    ngx_memcpy(&ctx->zone, zone, sizeof(ngx_shm_zone_t));\n\n    zp = ngx_array_push(lmcf->shm_zones);\n    if (zp == NULL) {\n        return NULL;\n    }\n\n    *zp = zone;\n\n    /* set zone init */\n    zone->init = ngx_http_lua_shared_memory_init;\n    zone->data = ctx;\n\n    lmcf->requires_shm = 1;\n\n    return &ctx->zone;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_shared_memory_init(ngx_shm_zone_t *shm_zone, void *data)\n{\n    ngx_http_lua_shm_zone_ctx_t *octx = data;\n    ngx_shm_zone_t              *ozone;\n    void                        *odata;\n\n    ngx_int_t                    rc;\n    volatile ngx_cycle_t        *saved_cycle;\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_http_lua_shm_zone_ctx_t *ctx;\n    ngx_shm_zone_t              *zone;\n\n    ctx = (ngx_http_lua_shm_zone_ctx_t *) shm_zone->data;\n    zone = &ctx->zone;\n\n    odata = NULL;\n    if (octx) {\n        ozone = &octx->zone;\n        odata = ozone->data;\n    }\n\n    zone->shm = shm_zone->shm;\n#if (nginx_version >= 1009000)\n    zone->noreuse = shm_zone->noreuse;\n#endif\n\n    if (zone->init(zone, odata) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    dd(\"get lmcf\");\n\n    lmcf = ctx->lmcf;\n    if (lmcf == NULL) {\n        return NGX_ERROR;\n    }\n\n    dd(\"lmcf->lua: %p\", lmcf->lua);\n\n    lmcf->shm_zones_inited++;\n\n    if (lmcf->shm_zones_inited == lmcf->shm_zones->nelts\n        && lmcf->init_handler && !ngx_test_config)\n    {\n        saved_cycle = ngx_cycle;\n        ngx_cycle = ctx->cycle;\n\n        rc = lmcf->init_handler(ctx->log, lmcf, lmcf->lua);\n\n        ngx_cycle = saved_cycle;\n\n        if (rc != NGX_OK) {\n            /* an error happened */\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nngx_http_lua_co_ctx_t *\nngx_http_lua_get_cur_co_ctx(ngx_http_request_t *r)\n{\n    ngx_http_lua_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    return ctx == NULL ? NULL : ctx->cur_co_ctx;\n}\n\n\nvoid\nngx_http_lua_set_cur_co_ctx(ngx_http_request_t *r, ngx_http_lua_co_ctx_t *coctx)\n{\n    ngx_http_lua_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    coctx->data = r;\n\n    ctx->cur_co_ctx = coctx;\n}\n\n\nlua_State *\nngx_http_lua_get_co_ctx_vm(ngx_http_lua_co_ctx_t *coctx)\n{\n    return coctx->co;\n}\n\n\nvoid *\nngx_http_lua_get_co_ctx_data(ngx_http_lua_co_ctx_t *coctx)\n{\n    return coctx ? coctx->data : NULL;\n}\n\n\nvoid\nngx_http_lua_set_co_ctx_cleanup(ngx_http_lua_co_ctx_t *coctx,\n    ngx_http_cleanup_pt cleanup, void *data)\n{\n    coctx->cleanup = cleanup;\n    coctx->data = data;\n}\n\n\nvoid\nngx_http_lua_cleanup_co_ctx_pending_operation(ngx_http_lua_co_ctx_t *coctx)\n{\n    ngx_http_lua_cleanup_pending_operation(coctx);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_co_ctx_resume(ngx_http_request_t *r)\n{\n    lua_State                   *vm;\n    ngx_connection_t            *c;\n    ngx_int_t                    rc;\n    ngx_uint_t                   nreqs;\n    ngx_http_lua_ctx_t          *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->resume_handler = ngx_http_lua_wev_handler;\n\n    c = r->connection;\n    vm = ngx_http_lua_get_lua_vm(r, ctx);\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(vm, r, ctx, ctx->cur_co_ctx->nrets);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run thread returned %d\", rc);\n\n    if (rc == NGX_AGAIN) {\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (ctx->entered_content_phase) {\n        ngx_http_lua_finalize_request(r, rc);\n        return NGX_DONE;\n    }\n\n    return rc;\n}\n\n\nvoid\nngx_http_lua_co_ctx_resume_helper(ngx_http_lua_co_ctx_t *coctx, int nrets)\n{\n    ngx_connection_t        *c;\n    ngx_http_request_t      *r;\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_http_log_ctx_t      *log_ctx;\n\n    r = coctx->data;\n    c = r->connection;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (ctx == NULL) {\n        return;\n    }\n\n    if (c->fd != (ngx_socket_t) -1) {  /* not a fake connection */\n        log_ctx = c->log->data;\n        log_ctx->current_request = r;\n    }\n\n    coctx->nrets = nrets;\n    coctx->cleanup = NULL;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"lua coctx resume handler: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    ctx->cur_co_ctx = coctx;\n\n    if (ctx->entered_content_phase) {\n        (void) ngx_http_lua_co_ctx_resume(r);\n\n    } else {\n        ctx->resume_handler = ngx_http_lua_co_ctx_resume;\n        ngx_http_core_run_phases(r);\n    }\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nint\nngx_http_lua_get_lua_http10_buffering(ngx_http_request_t *r)\n{\n    ngx_http_lua_loc_conf_t      *llcf;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    return llcf->http10_buffering;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_args.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_args.h\"\n#include \"ngx_http_lua_util.h\"\n\n\nstatic int ngx_http_lua_ngx_req_set_uri_args(lua_State *L);\nstatic int ngx_http_lua_ngx_req_get_post_args(lua_State *L);\n\n\nuintptr_t\nngx_http_lua_escape_args(u_char *dst, u_char *src, size_t size)\n{\n    ngx_uint_t      n;\n    static u_char   hex[] = \"0123456789ABCDEF\";\n\n                    /* %00-%20 %7F*/\n\n    static uint32_t   escape[] = {\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0x00000001, /* 0000 0000 0000 0000  0000 0000 0000 0001 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */\n\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n    };\n\n    if (dst == NULL) {\n\n        /* find the number of the characters to be escaped */\n\n        n = 0;\n\n        while (size) {\n            if (escape[*src >> 5] & (1 << (*src & 0x1f))) {\n                n++;\n            }\n\n            src++;\n            size--;\n        }\n\n        return (uintptr_t) n;\n    }\n\n    while (size) {\n        if (escape[*src >> 5] & (1 << (*src & 0x1f))) {\n            *dst++ = '%';\n            *dst++ = hex[*src >> 4];\n            *dst++ = hex[*src & 0xf];\n            src++;\n\n        } else {\n            *dst++ = *src++;\n        }\n\n        size--;\n    }\n\n    return (uintptr_t) dst;\n}\n\n\nstatic int\nngx_http_lua_ngx_req_set_uri_args(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    ngx_str_t                    args;\n    const char                  *msg;\n    size_t                       len;\n    u_char                      *p;\n    uintptr_t                    escape;\n\n    if (lua_gettop(L) != 1) {\n        return luaL_error(L, \"expecting 1 argument but seen %d\",\n                          lua_gettop(L));\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request object found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    switch (lua_type(L, 1)) {\n    case LUA_TNUMBER:\n        p = (u_char *) lua_tolstring(L, 1, &len);\n\n        args.data = ngx_palloc(r->pool, len);\n        if (args.data == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        ngx_memcpy(args.data, p, len);\n\n        args.len = len;\n        break;\n\n    case LUA_TSTRING:\n        p = (u_char *) lua_tolstring(L, 1, &len);\n\n        escape = ngx_http_lua_escape_args(NULL, p, len);\n        if (escape > 0) {\n            args.len = len + 2 * escape;\n            args.data = ngx_palloc(r->pool, args.len);\n            if (args.data == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_http_lua_escape_args(args.data, p, len);\n\n        } else {\n            args.data = ngx_palloc(r->pool, len);\n            if (args.data == NULL) {\n                return luaL_error(L, \"no memory\");\n            }\n\n            ngx_memcpy(args.data, p, len);\n\n            args.len = len;\n        }\n\n        break;\n\n    case LUA_TTABLE:\n        ngx_http_lua_process_args_option(r, L, 1, &args);\n\n        dd(\"args: %.*s\", (int) args.len, args.data);\n\n        break;\n\n    default:\n        msg = lua_pushfstring(L, \"string, number, or table expected, \"\n                              \"but got %s\", luaL_typename(L, 1));\n        return luaL_argerror(L, 1, msg);\n    }\n\n    dd(\"args: %.*s\", (int) args.len, args.data);\n\n    r->args.data = args.data;\n    r->args.len = args.len;\n\n    r->valid_unparsed_uri = 0;\n\n    return 0;\n}\n\n\nstatic int\nngx_http_lua_ngx_req_get_post_args(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    u_char                      *buf;\n    int                          retval;\n    size_t                       len;\n    ngx_chain_t                 *cl;\n    u_char                      *p;\n    u_char                      *last;\n    int                          n;\n    int                          max;\n\n    n = lua_gettop(L);\n\n    if (n != 0 && n != 1) {\n        return luaL_error(L, \"expecting 0 or 1 arguments but seen %d\", n);\n    }\n\n    if (n == 1) {\n        max = luaL_checkinteger(L, 1);\n        lua_pop(L, 1);\n\n    } else {\n        max = NGX_HTTP_LUA_MAX_ARGS;\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request object found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    if (r->discard_body) {\n        lua_createtable(L, 0, 0);\n        return 1;\n    }\n\n    if (r->request_body == NULL) {\n        return luaL_error(L, \"no request body found; \"\n                          \"maybe you should turn on lua_need_request_body?\");\n    }\n\n    if (r->request_body->temp_file) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"request body in temp file not supported\");\n        return 2;\n    }\n\n    if (r->request_body->bufs == NULL) {\n        lua_createtable(L, 0, 0);\n        return 1;\n    }\n\n    /* we copy r->request_body->bufs over to buf to simplify\n     * unescaping query arg keys and values */\n\n    len = 0;\n    for (cl = r->request_body->bufs; cl; cl = cl->next) {\n        len += cl->buf->last - cl->buf->pos;\n    }\n\n    dd(\"post body length: %d\", (int) len);\n\n    if (len == 0) {\n        lua_createtable(L, 0, 0);\n        return 1;\n    }\n\n    buf = ngx_palloc(r->pool, len);\n    if (buf == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    lua_createtable(L, 0, 4);\n\n    p = buf;\n    for (cl = r->request_body->bufs; cl; cl = cl->next) {\n        p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos);\n    }\n\n    dd(\"post body: %.*s\", (int) len, buf);\n\n    last = buf + len;\n\n    retval = ngx_http_lua_parse_args(L, buf, last, max);\n\n    ngx_pfree(r->pool, buf);\n\n    return retval;\n}\n\n\nint\nngx_http_lua_parse_args(lua_State *L, u_char *buf, u_char *last, int max)\n{\n    u_char                      *p, *q;\n    u_char                      *src, *dst;\n    unsigned                     parsing_value;\n    size_t                       len;\n    int                          count = 0;\n    int                          top;\n\n    top = lua_gettop(L);\n\n    p = buf;\n\n    parsing_value = 0;\n    q = p;\n\n    while (p != last) {\n        if (*p == '=' && ! parsing_value) {\n            /* key data is between p and q */\n\n            src = q; dst = q;\n\n            ngx_http_lua_unescape_uri(&dst, &src, p - q,\n                                      NGX_UNESCAPE_URI_COMPONENT);\n\n            dd(\"pushing key %.*s\", (int) (dst - q), q);\n\n            /* push the key */\n            lua_pushlstring(L, (char *) q, dst - q);\n\n            /* skip the current '=' char */\n            p++;\n\n            q = p;\n            parsing_value = 1;\n\n        } else if (*p == '&') {\n            /* reached the end of a key or a value, just save it */\n            src = q; dst = q;\n\n            ngx_http_lua_unescape_uri(&dst, &src, p - q,\n                                      NGX_UNESCAPE_URI_COMPONENT);\n\n            dd(\"pushing key or value %.*s\", (int) (dst - q), q);\n\n            /* push the value or key */\n            lua_pushlstring(L, (char *) q, dst - q);\n\n            /* skip the current '&' char */\n            p++;\n\n            q = p;\n\n            if (parsing_value) {\n                /* end of the current pair's value */\n                parsing_value = 0;\n\n            } else {\n                /* the current parsing pair takes no value,\n                 * just push the value \"true\" */\n                dd(\"pushing boolean true\");\n\n                lua_pushboolean(L, 1);\n            }\n\n            (void) lua_tolstring(L, -2, &len);\n\n            if (len == 0) {\n                /* ignore empty string key pairs */\n                dd(\"popping key and value...\");\n                lua_pop(L, 2);\n\n            } else {\n                dd(\"setting table...\");\n                ngx_http_lua_set_multi_value_table(L, top);\n            }\n\n            if (max > 0 && ++count == max) {\n                lua_pushliteral(L, \"truncated\");\n\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                               \"lua hit query args limit %d\", max);\n                return 2;\n            }\n\n        } else {\n            p++;\n        }\n    }\n\n    if (p != q || parsing_value) {\n        src = q; dst = q;\n\n        ngx_http_lua_unescape_uri(&dst, &src, p - q,\n                                  NGX_UNESCAPE_URI_COMPONENT);\n\n        dd(\"pushing key or value %.*s\", (int) (dst - q), q);\n\n        lua_pushlstring(L, (char *) q, dst - q);\n\n        if (!parsing_value) {\n            dd(\"pushing boolean true...\");\n            lua_pushboolean(L, 1);\n        }\n\n        (void) lua_tolstring(L, -2, &len);\n\n        if (len == 0) {\n            /* ignore empty string key pairs */\n            dd(\"popping key and value...\");\n            lua_pop(L, 2);\n\n        } else {\n            dd(\"setting table...\");\n            ngx_http_lua_set_multi_value_table(L, top);\n        }\n    }\n\n    dd(\"gettop: %d\", lua_gettop(L));\n    dd(\"type: %s\", lua_typename(L, lua_type(L, 1)));\n\n    if (lua_gettop(L) != top) {\n        return luaL_error(L, \"internal error: stack in bad state\");\n    }\n\n    return 1;\n}\n\n\nvoid\nngx_http_lua_inject_req_args_api(lua_State *L)\n{\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_set_uri_args);\n    lua_setfield(L, -2, \"set_uri_args\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_get_post_args);\n    lua_setfield(L, -2, \"get_post_args\");\n}\n\n\nsize_t\nngx_http_lua_ffi_req_get_querystring_len(ngx_http_request_t *r)\n{\n    return r->args.len;\n}\n\n\nint\nngx_http_lua_ffi_req_get_uri_args_count(ngx_http_request_t *r, int max,\n    int *truncated)\n{\n    int                      count;\n    u_char                  *p, *last;\n\n    if (r->connection->fd == (ngx_socket_t) -1) {\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    *truncated = 0;\n\n    if (max < 0) {\n        max = NGX_HTTP_LUA_MAX_ARGS;\n    }\n\n    last = r->args.data + r->args.len;\n    count = 0;\n\n    for (p = r->args.data; p != last; p++) {\n        if (*p == '&') {\n            if (count == 0) {\n                count += 2;\n\n            } else {\n                count++;\n            }\n        }\n    }\n\n    if (count) {\n        if (max > 0 && count > max) {\n            count = max;\n            *truncated = 1;\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"lua hit query args limit %d\", max);\n        }\n\n        return count;\n    }\n\n    if (r->args.len) {\n        return 1;\n    }\n\n    return 0;\n}\n\n\nint\nngx_http_lua_ffi_req_get_uri_args(ngx_http_request_t *r, u_char *buf,\n    ngx_http_lua_ffi_table_elt_t *out, int count)\n{\n    int                          i, parsing_value = 0;\n    u_char                      *last, *p, *q;\n    u_char                      *src, *dst;\n\n    if (count <= 0) {\n        return NGX_OK;\n    }\n\n    ngx_memcpy(buf, r->args.data, r->args.len);\n\n    i = 0;\n    last = buf + r->args.len;\n    p = buf;\n    q = p;\n\n    while (p != last) {\n        if (*p == '=' && !parsing_value) {\n            /* key data is between p and q */\n\n            src = q; dst = q;\n\n            ngx_http_lua_unescape_uri(&dst, &src, p - q,\n                                      NGX_UNESCAPE_URI_COMPONENT);\n\n            dd(\"saving key %.*s\", (int) (dst - q), q);\n\n            out[i].key.data = q;\n            out[i].key.len = (int) (dst - q);\n\n            /* skip the current '=' char */\n            p++;\n\n            q = p;\n            parsing_value = 1;\n\n        } else if (*p == '&') {\n            /* reached the end of a key or a value, just save it */\n            src = q; dst = q;\n\n            ngx_http_lua_unescape_uri(&dst, &src, p - q,\n                                      NGX_UNESCAPE_URI_COMPONENT);\n\n            dd(\"pushing key or value %.*s\", (int) (dst - q), q);\n\n            if (parsing_value) {\n                /* end of the current pair's value */\n                parsing_value = 0;\n\n                if (out[i].key.len) {\n                    out[i].value.data = q;\n                    out[i].value.len = (int) (dst - q);\n                    i++;\n                }\n\n            } else {\n                /* the current parsing pair takes no value,\n                 * just push the value \"true\" */\n                dd(\"pushing boolean true\");\n\n                if (dst - q) {\n                    out[i].key.data = q;\n                    out[i].key.len = (int) (dst - q);\n                    out[i].value.len = -1;\n                    i++;\n                }\n            }\n\n            if (i == count) {\n                return i;\n            }\n\n            /* skip the current '&' char */\n            p++;\n\n            q = p;\n\n        } else {\n            p++;\n        }\n    }\n\n    if (p != q || parsing_value) {\n        src = q; dst = q;\n\n        ngx_http_lua_unescape_uri(&dst, &src, p - q,\n                                  NGX_UNESCAPE_URI_COMPONENT);\n\n        dd(\"pushing key or value %.*s\", (int) (dst - q), q);\n\n        if (parsing_value) {\n            if (out[i].key.len) {\n                out[i].value.data = q;\n                out[i].value.len = (int) (dst - q);\n                i++;\n            }\n\n        } else {\n            if (dst - q) {\n                out[i].key.data = q;\n                out[i].key.len = (int) (dst - q);\n                out[i].value.len = (int) -1;\n                i++;\n            }\n        }\n    }\n\n    return i;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_args.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_ARGS_H_INCLUDED_\n#define _NGX_HTTP_LUA_ARGS_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_req_args_api(lua_State *L);\nint ngx_http_lua_parse_args(lua_State *L, u_char *buf, u_char *last, int max);\n\n\n#endif /* _NGX_HTTP_LUA_ARGS_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_balancer.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_balancer.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_directive.h\"\n\n#define NGX_BALANCER_DEF_HOST_LEN  32\ntypedef struct {\n    ngx_queue_t                    queue;\n    ngx_queue_t                    hnode;\n    ngx_uint_t                     hash;\n    ngx_http_lua_srv_conf_t       *lscf;\n    ngx_connection_t              *connection;\n    socklen_t                      socklen;\n    ngx_sockaddr_t                 sockaddr;\n    ngx_sockaddr_t                 local_sockaddr;\n    ngx_str_t                      host;\n    /* try to avoid allocating memory from the connection pool */\n    u_char                         host_data[NGX_BALANCER_DEF_HOST_LEN];\n} ngx_http_lua_balancer_ka_item_t; /*balancer keepalive item*/\n\n\nstruct ngx_http_lua_balancer_peer_data_s {\n    ngx_uint_t                          keepalive_requests;\n    ngx_msec_t                          keepalive_timeout;\n\n    void                               *data;\n\n    ngx_event_get_peer_pt               original_get_peer;\n    ngx_event_free_peer_pt              original_free_peer;\n\n#if (NGX_HTTP_SSL)\n    ngx_event_set_peer_session_pt       original_set_session;\n    ngx_event_save_peer_session_pt      original_save_session;\n#endif\n\n    ngx_http_lua_srv_conf_t            *conf;\n    ngx_http_request_t                 *request;\n\n    ngx_uint_t                          more_tries;\n    ngx_uint_t                          total_tries;\n\n    struct sockaddr                    *sockaddr;\n    socklen_t                           socklen;\n    ngx_addr_t                         *local;\n\n    ngx_str_t                           host;\n    ngx_str_t                          *addr_text;\n\n    int                                 last_peer_state;\n\n#if !(HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)\n    unsigned                            cloned_upstream_conf:1;\n#endif\n\n    unsigned                            keepalive:1;\n};\n\n\n#if (NGX_HTTP_SSL)\nstatic ngx_int_t ngx_http_lua_balancer_set_session(ngx_peer_connection_t *pc,\n    void *data);\nstatic void ngx_http_lua_balancer_save_session(ngx_peer_connection_t *pc,\n    void *data);\nstatic ngx_int_t\nngx_http_lua_upstream_get_ssl_name(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\n#endif\nstatic ngx_int_t ngx_http_lua_balancer_init(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_lua_balancer_init_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_lua_balancer_get_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic ngx_int_t ngx_http_lua_balancer_by_chunk(lua_State *L,\n    ngx_http_request_t *r);\nstatic void ngx_http_lua_balancer_free_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t state);\nstatic void ngx_http_lua_balancer_notify_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t type);\nstatic void ngx_http_lua_balancer_close(ngx_connection_t *c);\nstatic void ngx_http_lua_balancer_dummy_handler(ngx_event_t *ev);\nstatic void ngx_http_lua_balancer_close_handler(ngx_event_t *ev);\nstatic ngx_connection_t *ngx_http_lua_balancer_get_cached_item(\n    ngx_http_lua_srv_conf_t *lscf, ngx_peer_connection_t *pc, ngx_str_t *name);\nstatic ngx_uint_t ngx_http_lua_balancer_calc_hash(ngx_str_t *name,\n    struct sockaddr *sockaddr, socklen_t socklen, ngx_addr_t *local);\n\n\nstatic struct sockaddr  *ngx_http_lua_balancer_default_server_sockaddr;\n\n\nngx_int_t\nngx_http_lua_balancer_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L,\n                                     lscf->balancer.src.data,\n                                     &lscf->balancer.src_ref,\n                                     lscf->balancer.src_key);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_balancer_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_balancer_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       lscf->balancer.src.data,\n                                       lscf->balancer.src.len,\n                                       &lscf->balancer.src_ref,\n                                       lscf->balancer.src_key,\n                                       (const char *) lscf->balancer.chunkname);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_balancer_by_chunk(L, r);\n}\n\n\nchar *\nngx_http_lua_balancer_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_balancer_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    size_t                       chunkname_len;\n    u_char                      *chunkname;\n    u_char                      *cache_key = NULL;\n    u_char                      *name;\n    ngx_str_t                   *value;\n    ngx_http_lua_srv_conf_t     *lscf = conf;\n    ngx_url_t                    url;\n\n    ngx_http_upstream_srv_conf_t      *uscf;\n    ngx_http_upstream_server_t        *us;\n\n    dd(\"enter\");\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (lscf->balancer.handler) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    lscf->balancer.handler = (ngx_http_lua_srv_conf_handler_pt) cmd->post;\n\n    if (cmd->post == ngx_http_lua_balancer_handler_file) {\n        /* Lua code in an external file */\n        name = ngx_http_lua_rebase_path(cf->pool, value[1].data,\n                                        value[1].len);\n        if (name == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                    value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        lscf->balancer.src.data = name;\n        lscf->balancer.src.len = ngx_strlen(name);\n\n    } else {\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf, \"balancer_by_lua\",\n                                                     value[1].data,\n                                                     value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"balancer_by_lua\",\n                                                sizeof(\"balancer_by_lua\") - 1,\n                                                &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        lscf->balancer.src = value[1];\n        lscf->balancer.chunkname = chunkname;\n    }\n\n    lscf->balancer.src_key = cache_key;\n\n    /* balancer setup */\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    if (uscf->servers->nelts == 0) {\n        us = ngx_array_push(uscf->servers);\n        if (us == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_memzero(us, sizeof(ngx_http_upstream_server_t));\n        ngx_memzero(&url, sizeof(ngx_url_t));\n\n        ngx_str_set(&url.url, \"0.0.0.1\");\n        url.default_port = 80;\n\n        if (ngx_parse_url(cf->pool, &url) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        us->name = url.url;\n        us->addrs = url.addrs;\n        us->naddrs = url.naddrs;\n\n        ngx_http_lua_balancer_default_server_sockaddr = us->addrs[0].sockaddr;\n    }\n\n    if (uscf->peer.init_upstream) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"load balancing method redefined\");\n\n        lscf->balancer.original_init_upstream = uscf->peer.init_upstream;\n\n    } else {\n        lscf->balancer.original_init_upstream =\n            ngx_http_upstream_init_round_robin;\n    }\n\n    uscf->peer.init_upstream = ngx_http_lua_balancer_init;\n\n    uscf->flags = NGX_HTTP_UPSTREAM_CREATE\n                  |NGX_HTTP_UPSTREAM_WEIGHT\n                  |NGX_HTTP_UPSTREAM_MAX_FAILS\n                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT\n                  |NGX_HTTP_UPSTREAM_DOWN;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_balancer_init(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_uint_t                            i;\n    ngx_uint_t                            bucket_cnt;\n    ngx_queue_t                          *buckets;\n    ngx_http_lua_srv_conf_t              *lscf;\n    ngx_http_lua_balancer_ka_item_t      *cached;\n\n    lscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_lua_module);\n\n    ngx_conf_init_uint_value(lscf->balancer.max_cached, 32);\n\n    if (lscf->balancer.original_init_upstream(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    lscf->balancer.original_init_peer = us->peer.init;\n\n    us->peer.init = ngx_http_lua_balancer_init_peer;\n\n    /* allocate cache items and add to free queue */\n\n    cached = ngx_pcalloc(cf->pool,\n                         sizeof(ngx_http_lua_balancer_ka_item_t)\n                         * lscf->balancer.max_cached);\n    if (cached == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_queue_init(&lscf->balancer.cache);\n    ngx_queue_init(&lscf->balancer.free);\n\n    for (i = 0; i < lscf->balancer.max_cached; i++) {\n        ngx_queue_insert_head(&lscf->balancer.free, &cached[i].queue);\n        cached[i].lscf = lscf;\n    }\n\n    bucket_cnt = lscf->balancer.max_cached / 2;\n    bucket_cnt = bucket_cnt > 0 ? bucket_cnt : 1;\n    buckets = ngx_pcalloc(cf->pool, sizeof(ngx_queue_t) * bucket_cnt);\n\n    if (buckets == NULL) {\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < bucket_cnt; i++) {\n        ngx_queue_init(&buckets[i]);\n    }\n\n    lscf->balancer.buckets = buckets;\n    lscf->balancer.bucket_cnt = bucket_cnt;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_balancer_init_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_http_lua_srv_conf_t            *lscf;\n    ngx_http_lua_balancer_peer_data_t  *bp;\n\n    lscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_lua_module);\n\n    if (lscf->balancer.original_init_peer(r, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    bp = ngx_pcalloc(r->pool, sizeof(ngx_http_lua_balancer_peer_data_t));\n    if (bp == NULL) {\n        return NGX_ERROR;\n    }\n\n    bp->conf = lscf;\n    bp->request = r;\n    bp->data = r->upstream->peer.data;\n    bp->original_get_peer = r->upstream->peer.get;\n    bp->original_free_peer = r->upstream->peer.free;\n\n    r->upstream->peer.data = bp;\n    r->upstream->peer.get = ngx_http_lua_balancer_get_peer;\n    r->upstream->peer.free = ngx_http_lua_balancer_free_peer;\n    r->upstream->peer.notify = ngx_http_lua_balancer_notify_peer;\n\n#if (NGX_HTTP_SSL)\n    bp->original_set_session = r->upstream->peer.set_session;\n    bp->original_save_session = r->upstream->peer.save_session;\n\n    r->upstream->peer.set_session = ngx_http_lua_balancer_set_session;\n    r->upstream->peer.save_session = ngx_http_lua_balancer_save_session;\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_balancer_get_peer(ngx_peer_connection_t *pc, void *data)\n{\n    void                               *pdata;\n    lua_State                          *L;\n    ngx_int_t                           rc;\n    ngx_connection_t                   *c;\n    ngx_http_request_t                 *r;\n#if (NGX_HTTP_SSL)\n    ngx_http_upstream_t                *u;\n#endif\n    ngx_http_lua_ctx_t                 *ctx;\n    ngx_http_lua_srv_conf_t            *lscf;\n    ngx_http_lua_balancer_peer_data_t  *bp = data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"lua balancer: get peer, tries: %ui\", pc->tries);\n\n    r = bp->request;\n#if (NGX_HTTP_SSL)\n    u = r->upstream;\n#endif\n    lscf = bp->conf;\n\n    ngx_http_lua_assert(lscf->balancer.handler && r);\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        L = ngx_http_lua_get_lua_vm(r, ctx);\n\n    } else {\n        L = ngx_http_lua_get_lua_vm(r, ctx);\n\n        dd(\"reset ctx\");\n        ngx_http_lua_reset_ctx(r, L, ctx);\n    }\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_BALANCER;\n\n    bp->sockaddr = NULL;\n    bp->socklen = 0;\n    bp->more_tries = 0;\n    bp->keepalive_requests = 0;\n    bp->keepalive_timeout = 0;\n    bp->keepalive = 0;\n    bp->total_tries++;\n\n    pdata = r->upstream->peer.data;\n    r->upstream->peer.data = bp;\n\n    rc = lscf->balancer.handler(r, lscf, L);\n\n    r->upstream->peer.data = pdata;\n\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (ctx->exited && ctx->exit_code != NGX_OK) {\n        rc = ctx->exit_code;\n        if (rc == NGX_ERROR\n            || rc == NGX_BUSY\n            || rc == NGX_DECLINED\n#ifdef HAVE_BALANCER_STATUS_CODE_PATCH\n            || rc >= NGX_HTTP_SPECIAL_RESPONSE\n#endif\n        ) {\n            return rc;\n        }\n\n        if (rc > NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (bp->local != NULL) {\n        pc->local = bp->local;\n    }\n\n    if (bp->sockaddr && bp->socklen) {\n        pc->sockaddr = bp->sockaddr;\n        pc->socklen = bp->socklen;\n        pc->name = bp->addr_text;\n        pc->cached = 0;\n        pc->connection = NULL;\n\n        if (bp->more_tries) {\n            r->upstream->peer.tries += bp->more_tries;\n        }\n\n        if (bp->keepalive) {\n#if (NGX_HTTP_SSL)\n            if (bp->host.len == 0 && u->ssl) {\n                ngx_http_lua_upstream_get_ssl_name(r, u);\n                bp->host = u->ssl_name;\n            }\n#endif\n\n            c = ngx_http_lua_balancer_get_cached_item(lscf, pc, &bp->host);\n\n            if (c) {\n                ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                               \"lua balancer: keepalive reusing connection %p,\"\n                               \" host: %V, name: %V\",\n                               c, bp->addr_text, &bp->host);\n                return NGX_DONE;\n            }\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                           \"lua balancer: keepalive no free connection, \"\n                           \"host: %V, name: %v\",  bp->addr_text, &bp->host);\n        }\n\n        return NGX_OK;\n    }\n\n    rc = bp->original_get_peer(pc, bp->data);\n    if (rc == NGX_ERROR) {\n        return rc;\n    }\n\n    if (pc->sockaddr == ngx_http_lua_balancer_default_server_sockaddr) {\n        ngx_log_error(NGX_LOG_ERR, pc->log, 0,\n                      \"lua balancer: no peer set\");\n\n        return NGX_ERROR;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_balancer_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    u_char                  *err_msg;\n    size_t                   len;\n    ngx_int_t                rc;\n\n    /* init nginx context in Lua VM */\n    ngx_http_lua_set_req(L, r);\n\n#ifndef OPENRESTY_LUAJIT\n    ngx_http_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);\n\n    /*  {{{ make new env inheriting main thread's globals table */\n    lua_createtable(L, 0, 1 /* nrec */);   /* the metatable for the new env */\n    ngx_http_lua_get_globals_table(L);\n    lua_setfield(L, -2, \"__index\");\n    lua_setmetatable(L, -2);    /*  setmetatable({}, {__index = _G}) */\n    /*  }}} */\n\n    lua_setfenv(L, -2);    /*  set new running env for the code closure */\n#endif /* OPENRESTY_LUAJIT */\n\n    lua_pushcfunction(L, ngx_http_lua_traceback);\n    lua_insert(L, 1);  /* put it under chunk and args */\n\n    /*  protected call user code */\n    rc = lua_pcall(L, 0, 1, 1);\n\n    lua_remove(L, 1);  /* remove traceback function */\n\n    dd(\"rc == %d\", (int) rc);\n\n    if (rc != 0) {\n        /*  error occurred when running loaded code */\n        err_msg = (u_char *) lua_tolstring(L, -1, &len);\n\n        if (err_msg == NULL) {\n            err_msg = (u_char *) \"unknown reason\";\n            len = sizeof(\"unknown reason\") - 1;\n        }\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"failed to run balancer_by_lua*: %*s\", len, err_msg);\n\n        lua_settop(L, 0); /*  clear remaining elems on stack */\n\n        return NGX_ERROR;\n    }\n\n    lua_settop(L, 0); /*  clear remaining elems on stack */\n    return rc;\n}\n\n\nstatic void\nngx_http_lua_balancer_free_peer(ngx_peer_connection_t *pc, void *data,\n    ngx_uint_t state)\n{\n    ngx_uint_t                                  hash;\n    ngx_str_t                                  *host;\n    ngx_queue_t                                *q;\n    ngx_connection_t                           *c;\n    ngx_http_upstream_t                        *u;\n    ngx_http_lua_balancer_ka_item_t            *item;\n    ngx_http_lua_balancer_peer_data_t          *bp = data;\n    ngx_http_lua_srv_conf_t                    *lscf = bp->conf;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"lua balancer: free peer, tries: %ui\", pc->tries);\n\n    u = bp->request->upstream;\n    c = pc->connection;\n\n    if (bp->sockaddr && bp->socklen) {\n        bp->last_peer_state = (int) state;\n\n        if (pc->tries) {\n            pc->tries--;\n        }\n\n        if (bp->keepalive) {\n            if (state & NGX_PEER_FAILED\n                || c == NULL\n                || c->read->eof\n                || c->read->error\n                || c->read->timedout\n                || c->write->error\n                || c->write->timedout)\n            {\n                goto invalid;\n            }\n\n            if (bp->keepalive_requests\n                && c->requests >= bp->keepalive_requests)\n            {\n                goto invalid;\n            }\n\n            if (!u->keepalive) {\n                goto invalid;\n            }\n\n            if (!u->request_body_sent) {\n                goto invalid;\n            }\n\n            if (ngx_terminate || ngx_exiting) {\n                goto invalid;\n            }\n\n            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                goto invalid;\n            }\n\n            if (ngx_queue_empty(&lscf->balancer.free)) {\n                q = ngx_queue_last(&lscf->balancer.cache);\n\n                item = ngx_queue_data(q, ngx_http_lua_balancer_ka_item_t,\n                                      queue);\n                ngx_queue_remove(q);\n                ngx_queue_remove(&item->hnode);\n\n                ngx_http_lua_balancer_close(item->connection);\n\n            } else {\n                q = ngx_queue_head(&lscf->balancer.free);\n                ngx_queue_remove(q);\n\n                item = ngx_queue_data(q, ngx_http_lua_balancer_ka_item_t,\n                                      queue);\n            }\n\n            host = &bp->host;\n            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                           \"lua balancer: keepalive saving connection %p, \"\n                           \"host: %V, name: %V\",\n                           c, bp->addr_text, host);\n\n            ngx_queue_insert_head(&lscf->balancer.cache, q);\n            hash = ngx_http_lua_balancer_calc_hash(host,\n                                                   bp->sockaddr, bp->socklen,\n                                                   bp->local);\n            item->hash = hash;\n            hash %= lscf->balancer.bucket_cnt;\n            ngx_queue_insert_head(&lscf->balancer.buckets[hash], &item->hnode);\n            item->connection = c;\n            pc->connection = NULL;\n\n            c->read->delayed = 0;\n            ngx_add_timer(c->read, bp->keepalive_timeout);\n\n            if (c->write->timer_set) {\n                ngx_del_timer(c->write);\n            }\n\n            c->write->handler = ngx_http_lua_balancer_dummy_handler;\n            c->read->handler = ngx_http_lua_balancer_close_handler;\n\n            c->data = item;\n            c->idle = 1;\n            c->log = ngx_cycle->log;\n            c->read->log = ngx_cycle->log;\n            c->write->log = ngx_cycle->log;\n            c->pool->log = ngx_cycle->log;\n\n            item->socklen = pc->socklen;\n            ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);\n            if (pc->local) {\n                ngx_memcpy(&item->local_sockaddr,\n                           pc->local->sockaddr, pc->local->socklen);\n\n            } else {\n                ngx_memzero(&item->local_sockaddr,\n                            sizeof(item->local_sockaddr));\n            }\n\n            if (host->data && host->len) {\n                if (host->len <= sizeof(item->host_data)) {\n                    ngx_memcpy(item->host_data, host->data, host->len);\n                    item->host.data = item->host_data;\n                    item->host.len = host->len;\n\n                } else {\n                    item->host.data = ngx_pstrdup(c->pool, host);\n                    if (item->host.data == NULL) {\n                        ngx_http_lua_balancer_close(c);\n\n                        ngx_queue_remove(&item->queue);\n                        ngx_queue_remove(&item->hnode);\n                        ngx_queue_insert_head(&item->lscf->balancer.free,\n                                              &item->queue);\n                        return;\n                    }\n\n                    item->host.len = host->len;\n                }\n\n            } else {\n                ngx_str_null(&item->host);\n            }\n\n            if (c->read->ready) {\n                ngx_http_lua_balancer_close_handler(c->read);\n            }\n\n            return;\n\ninvalid:\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                           \"lua balancer: keepalive not saving connection %p\",\n                           c);\n        }\n\n        return;\n    }\n\n    bp->original_free_peer(pc, bp->data, state);\n}\n\n\nstatic void\nngx_http_lua_balancer_notify_peer(ngx_peer_connection_t *pc, void *data,\n    ngx_uint_t type)\n{\n#ifdef NGX_HTTP_UPSTREAM_NOTIFY_CACHED_CONNECTION_ERROR\n    if (type == NGX_HTTP_UPSTREAM_NOTIFY_CACHED_CONNECTION_ERROR) {\n        pc->tries--;\n    }\n#endif\n}\n\n\nstatic void\nngx_http_lua_balancer_close(ngx_connection_t *c)\n{\n#if (NGX_HTTP_SSL)\n    if (c->ssl) {\n        c->ssl->no_wait_shutdown = 1;\n        c->ssl->no_send_shutdown = 1;\n\n        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {\n            c->ssl->handler = ngx_http_lua_balancer_close;\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"lua balancer: keepalive shutdown \"\n                           \"connection %p failed\", c);\n            return;\n        }\n    }\n#endif\n\n    ngx_destroy_pool(c->pool);\n    ngx_close_connection(c);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"lua balancer: keepalive closing connection %p\", c);\n}\n\n\nstatic void\nngx_http_lua_balancer_dummy_handler(ngx_event_t *ev)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"lua balancer: dummy handler\");\n}\n\n\nstatic void\nngx_http_lua_balancer_close_handler(ngx_event_t *ev)\n{\n    ngx_http_lua_balancer_ka_item_t     *item;\n\n    int                n;\n    char               buf[1];\n    ngx_connection_t  *c;\n\n    c = ev->data;\n    if (c->close || c->read->timedout) {\n        goto close;\n    }\n\n    n = recv(c->fd, buf, 1, MSG_PEEK);\n\n    if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {\n        ev->ready = 0;\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            goto close;\n        }\n\n        return;\n    }\n\nclose:\n\n    item = c->data;\n    c->log = ev->log;\n\n    ngx_http_lua_balancer_close(c);\n\n    ngx_queue_remove(&item->queue);\n    ngx_queue_remove(&item->hnode);\n    ngx_queue_insert_head(&item->lscf->balancer.free, &item->queue);\n}\n\n\n#if (NGX_HTTP_SSL)\n\nstatic ngx_int_t\nngx_http_lua_balancer_set_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_lua_balancer_peer_data_t  *bp = data;\n\n    if (bp->sockaddr && bp->socklen) {\n        /* TODO */\n        return NGX_OK;\n    }\n\n    return bp->original_set_session(pc, bp->data);\n}\n\n\nstatic void\nngx_http_lua_balancer_save_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_lua_balancer_peer_data_t  *bp = data;\n\n    if (bp->sockaddr && bp->socklen) {\n        /* TODO */\n        return;\n    }\n\n    bp->original_save_session(pc, bp->data);\n}\n\n#endif\n\n\nint\nngx_http_lua_ffi_balancer_set_current_peer(ngx_http_request_t *r,\n    const u_char *addr, size_t addr_len, int port,\n    const u_char *host, size_t host_len,\n    char **err)\n{\n    ngx_url_t              url;\n    ngx_http_lua_ctx_t    *ctx;\n    ngx_http_upstream_t   *u;\n\n    ngx_http_lua_balancer_peer_data_t  *bp;\n\n    if (r == NULL) {\n        *err = \"no request found\";\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    if (u == NULL) {\n        *err = \"no upstream found\";\n        return NGX_ERROR;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        *err = \"no ctx found\";\n        return NGX_ERROR;\n    }\n\n    if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) {\n        *err = \"API disabled in the current context\";\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&url, sizeof(ngx_url_t));\n\n    url.url.data = ngx_palloc(r->pool, addr_len);\n    if (url.url.data == NULL) {\n        *err = \"no memory\";\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(url.url.data, addr, addr_len);\n\n    url.url.len = addr_len;\n    url.default_port = (in_port_t) port;\n    url.uri_part = 0;\n    url.no_resolve = 1;\n\n    if (ngx_parse_url(r->pool, &url) != NGX_OK) {\n        if (url.err) {\n            *err = url.err;\n        }\n\n        return NGX_ERROR;\n    }\n\n    bp = (ngx_http_lua_balancer_peer_data_t *) u->peer.data;\n\n    if (url.addrs && url.addrs[0].sockaddr) {\n        bp->sockaddr = url.addrs[0].sockaddr;\n        bp->socklen = url.addrs[0].socklen;\n        bp->addr_text = &url.addrs[0].name;\n\n    } else {\n        *err = \"no host allowed\";\n        return NGX_ERROR;\n    }\n\n    if (host && host_len) {\n        bp->host.data = ngx_palloc(r->pool, host_len);\n        if (bp->host.data == NULL) {\n            *err = \"no memory\";\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(bp->host.data, host, host_len);\n        bp->host.len = host_len;\n\n#if (NGX_HTTP_SSL)\n        if (u->ssl) {\n            u->ssl_name = bp->host;\n        }\n#endif\n\n    } else {\n        ngx_str_null(&bp->host);\n    }\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_balancer_bind_to_local_addr(ngx_http_request_t *r,\n    const u_char *addr, size_t addr_len,\n    u_char *errbuf, size_t *errbuf_size)\n{\n    u_char                *p;\n    ngx_http_lua_ctx_t    *ctx;\n    ngx_http_upstream_t   *u;\n    ngx_int_t              rc;\n\n    ngx_http_lua_balancer_peer_data_t  *bp;\n\n    if (r == NULL) {\n        p = ngx_snprintf(errbuf, *errbuf_size, \"no request found\");\n        *errbuf_size = p - errbuf;\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    if (u == NULL) {\n        p = ngx_snprintf(errbuf, *errbuf_size, \"no upstream found\");\n        *errbuf_size = p - errbuf;\n        return NGX_ERROR;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        p = ngx_snprintf(errbuf, *errbuf_size, \"no ctx found\");\n        *errbuf_size = p - errbuf;\n        return NGX_ERROR;\n    }\n\n    if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) {\n        p = ngx_snprintf(errbuf, *errbuf_size,\n                         \"API disabled in the current context\");\n        *errbuf_size = p - errbuf;\n        return NGX_ERROR;\n    }\n\n    bp = (ngx_http_lua_balancer_peer_data_t *) u->peer.data;\n\n    if (bp->local == NULL) {\n        bp->local = ngx_palloc(r->pool, sizeof(ngx_addr_t) + addr_len);\n        if (bp->local == NULL) {\n            p = ngx_snprintf(errbuf, *errbuf_size, \"no memory\");\n            *errbuf_size = p - errbuf;\n            return NGX_ERROR;\n        }\n    }\n\n    rc = ngx_parse_addr_port(r->pool, bp->local, (u_char *) addr, addr_len);\n    if (rc == NGX_ERROR) {\n        p = ngx_snprintf(errbuf, *errbuf_size, \"invalid addr %s\", addr);\n        *errbuf_size = p - errbuf;\n        return NGX_ERROR;\n    }\n\n    bp->local->name.len = addr_len;\n    bp->local->name.data = (u_char *) (bp->local + 1);\n    ngx_memcpy(bp->local->name.data, addr, addr_len);\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_balancer_enable_keepalive(ngx_http_request_t *r,\n    unsigned long timeout, unsigned int max_requests, char **err)\n{\n    ngx_http_upstream_t                     *u;\n    ngx_http_lua_ctx_t                      *ctx;\n    ngx_http_lua_balancer_peer_data_t       *bp;\n\n    if (r == NULL) {\n        *err = \"no request found\";\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    if (u == NULL) {\n        *err = \"no upstream found\";\n        return NGX_ERROR;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        *err = \"no ctx found\";\n        return NGX_ERROR;\n    }\n\n    if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) {\n        *err = \"API disabled in the current context\";\n        return NGX_ERROR;\n    }\n\n    bp = (ngx_http_lua_balancer_peer_data_t *) u->peer.data;\n\n    if (!(bp->sockaddr && bp->socklen)) {\n        *err = \"no current peer set\";\n        return NGX_ERROR;\n    }\n\n    bp->keepalive_timeout = (ngx_msec_t) timeout;\n    bp->keepalive_requests = (ngx_uint_t) max_requests;\n    bp->keepalive = 1;\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_balancer_set_timeouts(ngx_http_request_t *r,\n    long connect_timeout, long send_timeout, long read_timeout,\n    char **err)\n{\n    ngx_http_lua_ctx_t                 *ctx;\n    ngx_http_upstream_t                *u;\n\n#if !(HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)\n    ngx_http_upstream_conf_t           *ucf;\n    ngx_http_lua_balancer_peer_data_t  *bp;\n#endif\n\n    if (r == NULL) {\n        *err = \"no request found\";\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    if (u == NULL) {\n        *err = \"no upstream found\";\n        return NGX_ERROR;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        *err = \"no ctx found\";\n        return NGX_ERROR;\n    }\n\n    if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) {\n        *err = \"API disabled in the current context\";\n        return NGX_ERROR;\n    }\n\n#if !(HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)\n    bp = (ngx_http_lua_balancer_peer_data_t *) u->peer.data;\n\n    if (!bp->cloned_upstream_conf) {\n        /* we clone the upstream conf for the current request so that\n         * we do not affect other requests at all. */\n\n        ucf = ngx_palloc(r->pool, sizeof(ngx_http_upstream_conf_t));\n\n        if (ucf == NULL) {\n            *err = \"no memory\";\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(ucf, u->conf, sizeof(ngx_http_upstream_conf_t));\n\n        u->conf = ucf;\n        bp->cloned_upstream_conf = 1;\n\n    } else {\n        ucf = u->conf;\n    }\n#endif\n\n    if (connect_timeout > 0) {\n#if (HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)\n        u->connect_timeout = (ngx_msec_t) connect_timeout;\n#else\n        ucf->connect_timeout = (ngx_msec_t) connect_timeout;\n#endif\n    }\n\n    if (send_timeout > 0) {\n#if (HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)\n        u->send_timeout = (ngx_msec_t) send_timeout;\n#else\n        ucf->send_timeout = (ngx_msec_t) send_timeout;\n#endif\n    }\n\n    if (read_timeout > 0) {\n#if (HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)\n        u->read_timeout = (ngx_msec_t) read_timeout;\n#else\n        ucf->read_timeout = (ngx_msec_t) read_timeout;\n#endif\n    }\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_balancer_set_more_tries(ngx_http_request_t *r,\n    int count, char **err)\n{\n#if (nginx_version >= 1007005)\n    ngx_uint_t                          max_tries, total;\n#endif\n    ngx_http_lua_ctx_t                 *ctx;\n    ngx_http_upstream_t                *u;\n    ngx_http_lua_balancer_peer_data_t  *bp;\n\n    if (r == NULL) {\n        *err = \"no request found\";\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    if (u == NULL) {\n        *err = \"no upstream found\";\n        return NGX_ERROR;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        *err = \"no ctx found\";\n        return NGX_ERROR;\n    }\n\n    if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) {\n        *err = \"API disabled in the current context\";\n        return NGX_ERROR;\n    }\n\n    bp = (ngx_http_lua_balancer_peer_data_t *) u->peer.data;\n\n#if (nginx_version >= 1007005)\n    max_tries = r->upstream->conf->next_upstream_tries;\n    total = bp->total_tries + r->upstream->peer.tries - 1;\n\n    if (max_tries && total + count > max_tries) {\n        count = max_tries - total;\n        *err = \"reduced tries due to limit\";\n\n    } else {\n        *err = NULL;\n    }\n#else\n    *err = NULL;\n#endif\n\n    bp->more_tries = count;\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_balancer_get_last_failure(ngx_http_request_t *r,\n    int *status, char **err)\n{\n    ngx_http_lua_ctx_t                 *ctx;\n    ngx_http_upstream_t                *u;\n    ngx_http_upstream_state_t          *state;\n    ngx_http_lua_balancer_peer_data_t  *bp;\n\n    if (r == NULL) {\n        *err = \"no request found\";\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    if (u == NULL) {\n        *err = \"no upstream found\";\n        return NGX_ERROR;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        *err = \"no ctx found\";\n        return NGX_ERROR;\n    }\n\n    if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) {\n        *err = \"API disabled in the current context\";\n        return NGX_ERROR;\n    }\n\n    bp = (ngx_http_lua_balancer_peer_data_t *) u->peer.data;\n\n    if (r->upstream_states && r->upstream_states->nelts > 1) {\n        state = r->upstream_states->elts;\n        *status = (int) state[r->upstream_states->nelts - 2].status;\n\n    } else {\n        *status = 0;\n    }\n\n    return bp->last_peer_state;\n}\n\n\nint\nngx_http_lua_ffi_balancer_recreate_request(ngx_http_request_t *r,\n    char **err)\n{\n    ngx_http_lua_ctx_t    *ctx;\n    ngx_http_upstream_t   *u;\n\n    if (r == NULL) {\n        *err = \"no request found\";\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    if (u == NULL) {\n        *err = \"no upstream found\";\n        return NGX_ERROR;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        *err = \"no ctx found\";\n        return NGX_ERROR;\n    }\n\n    if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) {\n        *err = \"API disabled in the current context\";\n        return NGX_ERROR;\n    }\n\n    /* u->create_request can not be NULL since we are in balancer phase */\n    ngx_http_lua_assert(u->create_request != NULL);\n\n    *err = NULL;\n\n    if (u->request_bufs != NULL && u->request_bufs != r->request_body->bufs) {\n        /* u->request_bufs already contains a valid request buffer\n         * remove it from chain first\n         */\n        u->request_bufs = r->request_body->bufs;\n    }\n\n    return u->create_request(r);\n}\n\n\nint\nngx_http_lua_ffi_balancer_set_upstream_tls(ngx_http_request_t *r, int on,\n    char **err)\n{\n    ngx_http_lua_ctx_t    *ctx;\n    ngx_http_upstream_t   *u;\n\n    if (r == NULL) {\n        *err = \"no request found\";\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    if (u == NULL) {\n        *err = \"no upstream found\";\n        return NGX_ERROR;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        *err = \"no ctx found\";\n        return NGX_ERROR;\n    }\n\n    if ((ctx->context & NGX_HTTP_LUA_CONTEXT_BALANCER) == 0) {\n        *err = \"API disabled in the current context\";\n        return NGX_ERROR;\n    }\n\n    if (on == 0) {\n        u->ssl = 0;\n        u->schema.len = sizeof(\"http://\") - 1;\n\n    } else {\n        u->ssl = 1;\n        u->schema.len = sizeof(\"https://\") - 1;\n    }\n\n    return NGX_OK;\n}\n\n\nchar *\nngx_http_lua_balancer_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_int_t    n;\n    ngx_str_t   *value;\n\n#if 0\n    ngx_http_upstream_srv_conf_t            *uscf;\n#endif\n    ngx_http_lua_srv_conf_t                 *lscf = conf;\n\n    if (lscf->balancer.max_cached != NGX_CONF_UNSET_UINT) {\n        return \"is duplicate\";\n    }\n\n    /* read options */\n\n    value = cf->args->elts;\n\n    n = ngx_atoi(value[1].data, value[1].len);\n\n    if (n == NGX_ERROR || n == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid value \\\"%V\\\" in \\\"%V\\\" directive\",\n                           &value[1], &cmd->name);\n        return NGX_CONF_ERROR;\n    }\n\n    lscf->balancer.max_cached = n;\n\n    return NGX_CONF_OK;\n}\n\n\n#if (NGX_HTTP_SSL)\nstatic ngx_int_t\nngx_http_lua_upstream_get_ssl_name(ngx_http_request_t *r,\n    ngx_http_upstream_t *u)\n{\n    u_char     *p, *last;\n    ngx_str_t   name;\n\n    if (u->conf->ssl_name) {\n        if (ngx_http_complex_value(r, u->conf->ssl_name, &name) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n    } else {\n        name = u->ssl_name;\n    }\n\n    if (name.len == 0) {\n        goto done;\n    }\n\n    /*\n     * ssl name here may contain port, notably if derived from $proxy_host\n     * or $http_host; we have to strip it. eg: www.example.com:443\n     */\n\n    p = name.data;\n    last = name.data + name.len;\n\n    if (*p == '[') {\n        p = ngx_strlchr(p, last, ']');\n\n        if (p == NULL) {\n            p = name.data;\n        }\n    }\n\n    p = ngx_strlchr(p, last, ':');\n\n    if (p != NULL) {\n        name.len = p - name.data;\n    }\n\ndone:\n\n    u->ssl_name = name;\n\n    return NGX_OK;\n}\n#endif\n\n\nstatic ngx_uint_t\nngx_http_lua_balancer_calc_hash(ngx_str_t *name,\n    struct sockaddr *sockaddr, socklen_t socklen, ngx_addr_t *local)\n{\n    ngx_uint_t hash;\n\n    hash = ngx_hash_key_lc(name->data, name->len);\n    hash ^= ngx_hash_key((u_char *) sockaddr, socklen);\n    if (local != NULL) {\n        hash ^= ngx_hash_key((u_char *) local->sockaddr, local->socklen);\n    }\n\n    return hash;\n}\n\n\nstatic ngx_connection_t *\nngx_http_lua_balancer_get_cached_item(ngx_http_lua_srv_conf_t *lscf,\n    ngx_peer_connection_t *pc, ngx_str_t *name)\n{\n    ngx_uint_t                         hash;\n    ngx_queue_t                       *q;\n    ngx_queue_t                       *head;\n    ngx_connection_t                  *c;\n    struct sockaddr                   *sockaddr;\n    socklen_t                          socklen;\n    ngx_addr_t                        *local;\n    ngx_http_lua_balancer_ka_item_t   *item;\n\n    sockaddr = pc->sockaddr;\n    socklen = pc->socklen;\n    local = pc->local;\n\n    hash = ngx_http_lua_balancer_calc_hash(name, sockaddr, socklen, pc->local);\n    head = &lscf->balancer.buckets[hash % lscf->balancer.bucket_cnt];\n\n    c = NULL;\n    for (q = ngx_queue_head(head);\n        q != ngx_queue_sentinel(head);\n        q = ngx_queue_next(q))\n    {\n        item = ngx_queue_data(q, ngx_http_lua_balancer_ka_item_t, hnode);\n        if (item->hash != hash) {\n            continue;\n        }\n\n        if (name->len == item->host.len\n            && ngx_memn2cmp((u_char *) &item->sockaddr,\n                            (u_char *) sockaddr,\n                            item->socklen, socklen) == 0\n            && ngx_strncasecmp(name->data,\n                               item->host.data, name->len) == 0\n            && (local == NULL\n                || ngx_memn2cmp((u_char *) &item->local_sockaddr,\n                                (u_char *) local->sockaddr,\n                                socklen, local->socklen) == 0))\n        {\n            c = item->connection;\n            ngx_queue_remove(q);\n            ngx_queue_remove(&item->queue);\n            ngx_queue_insert_head(&lscf->balancer.free, &item->queue);\n            c->idle = 0;\n            c->sent = 0;\n            c->log = pc->log;\n            c->read->log = pc->log;\n            c->write->log = pc->log;\n            c->pool->log = pc->log;\n\n            if (c->read->timer_set) {\n                ngx_del_timer(c->read);\n            }\n\n            pc->cached = 1;\n            pc->connection = c;\n            return c;\n        }\n    }\n\n    return NULL;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_balancer.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_BALANCER_H_INCLUDED_\n#define _NGX_HTTP_LUA_BALANCER_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_balancer_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L);\n\nngx_int_t ngx_http_lua_balancer_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L);\n\nchar *ngx_http_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nchar *ngx_http_lua_balancer_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nchar *ngx_http_lua_balancer_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n#endif /* _NGX_HTTP_LUA_BALANCER_H_INCLUDED_ */\n"
  },
  {
    "path": "src/ngx_http_lua_bodyfilterby.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_bodyfilterby.h\"\n#include \"ngx_http_lua_exception.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_pcrefix.h\"\n#include \"ngx_http_lua_log.h\"\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_headers.h\"\n#include \"ngx_http_lua_string.h\"\n#include \"ngx_http_lua_misc.h\"\n#include \"ngx_http_lua_consts.h\"\n#include \"ngx_http_lua_output.h\"\n\n\nstatic void ngx_http_lua_body_filter_by_lua_env(lua_State *L,\n    ngx_http_request_t *r, ngx_chain_t *in);\nstatic ngx_http_output_body_filter_pt ngx_http_next_body_filter;\n\n\n/**\n * Set environment table for the given code closure.\n *\n * Before:\n *         | code closure | <- top\n *         |      ...     |\n *\n * After:\n *         | code closure | <- top\n *         |      ...     |\n * */\nstatic void\nngx_http_lua_body_filter_by_lua_env(lua_State *L, ngx_http_request_t *r,\n    ngx_chain_t *in)\n{\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    ngx_http_lua_set_req(L, r);\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n    lmcf->body_filter_chain = in;\n\n#ifndef OPENRESTY_LUAJIT\n    /**\n     * we want to create empty environment for current script\n     *\n     * setmetatable({}, {__index = _G})\n     *\n     * if a function or symbol is not defined in our env, __index will lookup\n     * in the global env.\n     *\n     * all variables created in the script-env will be thrown away at the end\n     * of the script run.\n     * */\n    ngx_http_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);\n\n    /*  {{{ make new env inheriting main thread's globals table */\n    lua_createtable(L, 0, 1 /* nrec */);    /*  the metatable for the new\n                                                env */\n    ngx_http_lua_get_globals_table(L);\n    lua_setfield(L, -2, \"__index\");\n    lua_setmetatable(L, -2);    /*  setmetatable({}, {__index = _G}) */\n    /*  }}} */\n\n    lua_setfenv(L, -2);    /*  set new running env for the code closure */\n#endif /* OPENRESTY_LUAJIT */\n}\n\n\nngx_int_t\nngx_http_lua_body_filter_by_chunk(lua_State *L, ngx_http_request_t *r,\n    ngx_chain_t *in)\n{\n    ngx_int_t        rc;\n    u_char          *err_msg;\n    size_t           len;\n#if (NGX_PCRE)\n    ngx_pool_t      *old_pool;\n#endif\n\n    dd(\"initialize nginx context in Lua VM, code chunk at stack top  sp = 1\");\n    ngx_http_lua_body_filter_by_lua_env(L, r, in);\n\n#if (NGX_PCRE)\n    /* XXX: work-around to nginx regex subsystem */\n    old_pool = ngx_http_lua_pcre_malloc_init(r->pool);\n#endif\n\n    lua_pushcfunction(L, ngx_http_lua_traceback);\n    lua_insert(L, 1);  /* put it under chunk and args */\n\n    dd(\"protected call user code\");\n    rc = lua_pcall(L, 0, 1, 1);\n\n    lua_remove(L, 1);  /* remove traceback function */\n\n#if (NGX_PCRE)\n    /* XXX: work-around to nginx regex subsystem */\n    ngx_http_lua_pcre_malloc_done(old_pool);\n#endif\n\n    if (rc != 0) {\n\n        /*  error occurred */\n        err_msg = (u_char *) lua_tolstring(L, -1, &len);\n\n        if (err_msg == NULL) {\n            err_msg = (u_char *) \"unknown reason\";\n            len = sizeof(\"unknown reason\") - 1;\n        }\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"failed to run body_filter_by_lua*: %*s\", len, err_msg);\n\n        lua_settop(L, 0);    /*  clear remaining elems on stack */\n\n        return NGX_ERROR;\n    }\n\n    /* rc == 0 */\n\n    rc = (ngx_int_t) lua_tointeger(L, -1);\n\n    dd(\"got return value: %d\", (int) rc);\n\n    lua_settop(L, 0);\n\n    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_lua_body_filter_inline(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    lua_State                   *L;\n    ngx_int_t                    rc;\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->body_filter_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua inline script (w/ cache) sp = 1 */\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       llcf->body_filter_src.value.data,\n                                       llcf->body_filter_src.value.len,\n                                       &llcf->body_filter_src_ref,\n                                       llcf->body_filter_src_key,\n                                       (const char *)\n                                       llcf->body_filter_chunkname);\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_http_lua_body_filter_by_chunk(L, r, in);\n\n    dd(\"body filter by chunk returns %d\", (int) rc);\n\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_lua_body_filter_file(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    lua_State                       *L;\n    ngx_int_t                        rc;\n    u_char                          *script_path;\n    ngx_http_lua_loc_conf_t         *llcf;\n    ngx_str_t                        eval_src;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    /* Eval nginx variables in code path string first */\n    if (ngx_http_complex_value(r, &llcf->body_filter_src, &eval_src)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,\n                                           eval_src.len);\n\n    if (script_path == NULL) {\n        return NGX_ERROR;\n    }\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->body_filter_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua script file (w/ cache)        sp = 1 */\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,\n                                     &llcf->body_filter_src_ref,\n                                     llcf->body_filter_src_key);\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    rc = ngx_http_lua_body_filter_by_chunk(L, r, in);\n\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_http_lua_loc_conf_t     *llcf;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_int_t                    rc;\n    uint16_t                     old_context;\n    ngx_pool_cleanup_t          *cln;\n    ngx_chain_t                 *out;\n    ngx_chain_t                 *cl, *ln;\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua body filter for user lua code, uri \\\"%V\\\"\", &r->uri);\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->body_filter_handler == NULL || r->header_only) {\n        dd(\"no body filter handler found\");\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    dd(\"ctx = %p\", ctx);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (ctx->seen_last_in_filter) {\n        for (/* void */; in; in = in->next) {\n            dd(\"mark the buf as consumed: %d\", (int) ngx_buf_size(in->buf));\n            in->buf->pos = in->buf->last;\n            in->buf->file_pos = in->buf->file_last;\n        }\n\n        in = NULL;\n\n        /* continue to call ngx_http_next_body_filter to process cached data */\n    }\n\n    if (in != NULL\n        && ngx_chain_add_copy(r->pool, &ctx->filter_in_bufs, in) != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ctx->filter_busy_bufs != NULL\n        && (r->connection->buffered\n            & (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED)))\n    {\n        /* Socket write buffer was full on last write.\n         * Try to write the remain data, if still can not write\n         * do not execute body_filter_by_lua otherwise the `in` chain will be\n         * replaced by new content from lua and buf of `in` mark as consumed.\n         * And then ngx_output_chain will call the filter chain again which\n         * make all the data cached in the memory and long ngx_chain_t link\n         * cause CPU 100%.\n         */\n        rc = ngx_http_next_body_filter(r, NULL);\n\n        if (rc == NGX_ERROR) {\n            return rc;\n        }\n\n        out = NULL;\n        ngx_chain_update_chains(r->pool,\n                                &ctx->free_bufs, &ctx->filter_busy_bufs, &out,\n                                (ngx_buf_tag_t) &ngx_http_lua_body_filter);\n        if (rc != NGX_OK\n            && ctx->filter_busy_bufs != NULL\n            && (r->connection->buffered\n                & (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED)))\n        {\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"waiting body filter busy buffer to be sent\");\n            return NGX_AGAIN;\n        }\n\n        /* continue to process bufs in ctx->filter_in_bufs */\n    }\n\n    if (ctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n        if (cln == NULL) {\n            return NGX_ERROR;\n        }\n\n        cln->handler = ngx_http_lua_request_cleanup_handler;\n        cln->data = ctx;\n        ctx->cleanup = &cln->handler;\n    }\n\n    old_context = ctx->context;\n    ctx->context = NGX_HTTP_LUA_CONTEXT_BODY_FILTER;\n\n    in = ctx->filter_in_bufs;\n    ctx->filter_in_bufs = NULL;\n\n    if (in != NULL) {\n        dd(\"calling body filter handler\");\n        rc = llcf->body_filter_handler(r, in);\n\n        dd(\"calling body filter handler returned %d\", (int) rc);\n\n        ctx->context = old_context;\n\n        if (rc != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n        /* lmcf->body_filter_chain is the new buffer chain if\n         * body_filter_by_lua set new body content via ngx.arg[1] = new_content\n         * otherwise it is the original `in` buffer chain.\n         */\n        out = lmcf->body_filter_chain;\n\n        if (in != out) {\n            /* content of body was replaced in\n             * ngx_http_lua_body_filter_param_set and the buffers was marked\n             * as consumed.\n             */\n            for (cl = in; cl != NULL; cl = ln) {\n                ln = cl->next;\n                ngx_free_chain(r->pool, cl);\n            }\n\n            if (out == NULL) {\n                /* do not forward NULL to the next filters because the input is\n                 * not NULL */\n                return NGX_OK;\n            }\n        }\n\n    } else {\n        ctx->context = old_context;\n        out = NULL;\n    }\n\n    rc = ngx_http_next_body_filter(r, out);\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    ngx_chain_update_chains(r->pool,\n                            &ctx->free_bufs, &ctx->filter_busy_bufs, &out,\n                            (ngx_buf_tag_t) &ngx_http_lua_body_filter);\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_lua_body_filter_init(void)\n{\n    dd(\"calling body filter init\");\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_lua_body_filter;\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_get_body_filter_param_eof(ngx_http_request_t *r)\n{\n    ngx_chain_t         *cl;\n    ngx_chain_t         *in;\n\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n    in = lmcf->body_filter_chain;\n\n    /* asking for the eof argument */\n\n    for (cl = in; cl; cl = cl->next) {\n        if (cl->buf->last_buf || cl->buf->last_in_chain) {\n            return 1;\n        }\n    }\n\n    return 0;\n}\n\n\nint\nngx_http_lua_ffi_get_body_filter_param_body(ngx_http_request_t *r,\n    u_char **data_p, size_t *len_p)\n{\n    size_t               size;\n    ngx_chain_t         *cl;\n    ngx_buf_t           *b;\n    ngx_chain_t         *in;\n\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n    in = lmcf->body_filter_chain;\n\n    size = 0;\n\n    if (in == NULL) {\n        /* being a cleared chain on the Lua land */\n        *len_p = 0;\n        return NGX_OK;\n    }\n\n    if (in->next == NULL) {\n\n        dd(\"seen only single buffer\");\n\n        b = in->buf;\n        *data_p = b->pos;\n        *len_p = b->last - b->pos;\n        return NGX_OK;\n    }\n\n    dd(\"seen multiple buffers\");\n\n    for (cl = in; cl; cl = cl->next) {\n        b = cl->buf;\n\n        size += b->last - b->pos;\n\n        if (b->last_buf || b->last_in_chain) {\n            break;\n        }\n    }\n\n    /* the buf is need and is not allocated from Lua land yet, return with\n     * the actual size */\n    *len_p = size;\n    return NGX_AGAIN;\n}\n\n\nint\nngx_http_lua_ffi_copy_body_filter_param_body(ngx_http_request_t *r,\n    u_char *data)\n{\n    u_char              *p;\n    ngx_chain_t         *cl;\n    ngx_buf_t           *b;\n    ngx_chain_t         *in;\n\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n    in = lmcf->body_filter_chain;\n\n    for (p = data, cl = in; cl; cl = cl->next) {\n        b = cl->buf;\n        p = ngx_copy(p, b->pos, b->last - b->pos);\n\n        if (b->last_buf || b->last_in_chain) {\n            break;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx)\n{\n    int                      type;\n    int                      idx;\n    int                      found;\n    u_char                  *data;\n    size_t                   size;\n    unsigned                 last;\n    unsigned                 flush = 0;\n    ngx_buf_t               *b;\n    ngx_chain_t             *cl;\n    ngx_chain_t             *in;\n\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    idx = luaL_checkint(L, 2);\n\n    dd(\"index: %d\", idx);\n\n    if (idx != 1 && idx != 2) {\n        return luaL_error(L, \"bad index: %d\", idx);\n    }\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    if (idx == 2) {\n        /* overwriting the eof flag */\n        last = lua_toboolean(L, 3);\n\n        in = lmcf->body_filter_chain;\n\n        if (last) {\n            ctx->seen_last_in_filter = 1;\n\n            /* the \"in\" chain cannot be NULL except that we set arg[1] = \"\"\n             * before arg[2] = true\n             */\n            if (in == NULL) {\n                in = ngx_http_lua_chain_get_free_buf(r->connection->log,\n                                                     r->pool,\n                                                     &ctx->free_bufs, 0);\n                if (in == NULL) {\n                    return luaL_error(L, \"no memory\");\n                }\n\n                in->buf->tag = (ngx_buf_tag_t) &ngx_http_lua_body_filter;\n                lmcf->body_filter_chain = in;\n            }\n\n            /* we set the \"last_buf\" or \"last_in_chain\" flag\n             * in the last buf of \"in\" */\n            for (cl = in; cl; cl = cl->next) {\n                if (cl->next == NULL) {\n                    if (r == r->main) {\n                        cl->buf->last_buf = 1;\n\n                    } else {\n                        cl->buf->last_in_chain = 1;\n                    }\n\n                    break;\n                }\n            }\n\n        } else {\n            /* last == 0 */\n\n            found = 0;\n\n            for (cl = in; cl; cl = cl->next) {\n                b = cl->buf;\n\n                if (b->last_buf) {\n                    b->last_buf = 0;\n                    found = 1;\n                }\n\n                if (b->last_in_chain) {\n                    b->last_in_chain = 0;\n                    found = 1;\n                }\n\n                if (found && b->last == b->pos && !ngx_buf_in_memory(b)) {\n                    /* make it a special sync buf to make\n                     * ngx_http_write_filter_module happy. */\n                    b->sync = 1;\n                }\n            }\n\n            ctx->seen_last_in_filter = 0;\n        }\n\n        return 0;\n    }\n\n    /* idx == 1, overwriting the chunk data */\n\n    type = lua_type(L, 3);\n\n    switch (type) {\n    case LUA_TSTRING:\n    case LUA_TNUMBER:\n        data = (u_char *) lua_tolstring(L, 3, &size);\n        break;\n\n    case LUA_TNIL:\n        /* discard the buffers */\n\n        in = lmcf->body_filter_chain;\n\n        last = 0;\n\n        for (cl = in; cl; cl = cl->next) {\n            b = cl->buf;\n\n            if (b->flush) {\n                flush = 1;\n            }\n\n            if (b->last_in_chain || b->last_buf) {\n                last = 1;\n            }\n\n            dd(\"mark the buf as consumed: %d\", (int) ngx_buf_size(b));\n            b->pos = b->last;\n        }\n\n        /* cl == NULL */\n\n        goto done;\n\n    case LUA_TTABLE:\n        size = ngx_http_lua_calc_strlen_in_table(L, 3 /* index */, 3 /* arg */,\n                                                 1 /* strict */);\n        data = NULL;\n        break;\n\n    default:\n        return luaL_error(L, \"bad chunk data type: %s\",\n                          lua_typename(L, type));\n    }\n\n    in = lmcf->body_filter_chain;\n\n    last = 0;\n\n    for (cl = in; cl; cl = cl->next) {\n        b = cl->buf;\n\n        if (b->flush) {\n            flush = 1;\n        }\n\n        if (b->last_buf || b->last_in_chain) {\n            last = 1;\n        }\n\n        dd(\"mark the buf as consumed: %d\", (int) ngx_buf_size(cl->buf));\n        cl->buf->pos = cl->buf->last;\n    }\n\n    /* cl == NULL */\n\n    if (size == 0) {\n        goto done;\n    }\n\n    cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,\n                                         &ctx->free_bufs, size);\n    if (cl == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    cl->buf->tag = (ngx_buf_tag_t) &ngx_http_lua_body_filter;\n    if (type == LUA_TTABLE) {\n        cl->buf->last = ngx_http_lua_copy_str_in_table(L, 3, cl->buf->last);\n\n    } else {\n        cl->buf->last = ngx_copy(cl->buf->pos, data, size);\n    }\n\ndone:\n\n    if (last || flush) {\n        if (cl == NULL) {\n            cl = ngx_http_lua_chain_get_free_buf(r->connection->log,\n                                                 r->pool,\n                                                 &ctx->free_bufs, 0);\n            if (cl == NULL) {\n                return luaL_error(L, \"no memory\");\n            }\n\n            cl->buf->tag = (ngx_buf_tag_t) &ngx_http_lua_body_filter;\n        }\n\n        if (last) {\n            ctx->seen_last_in_filter = 1;\n\n            if (r == r->main) {\n                cl->buf->last_buf = 1;\n\n            } else {\n                cl->buf->last_in_chain = 1;\n            }\n        }\n\n        if (flush) {\n            cl->buf->flush = 1;\n        }\n    }\n\n    lmcf->body_filter_chain = cl;\n\n    return 0;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_bodyfilterby.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_BODYFILTERBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_BODYFILTERBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nextern ngx_http_output_body_filter_pt ngx_http_lua_next_filter_body_filter;\n\n\nngx_int_t ngx_http_lua_body_filter_init(void);\nngx_int_t ngx_http_lua_body_filter_by_chunk(lua_State *L,\n    ngx_http_request_t *r, ngx_chain_t *in);\nngx_int_t ngx_http_lua_body_filter_inline(ngx_http_request_t *r,\n    ngx_chain_t *in);\nngx_int_t ngx_http_lua_body_filter_file(ngx_http_request_t *r,\n    ngx_chain_t *in);\nint ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx);\n\n\n#endif /* _NGX_HTTP_LUA_BODYFILTERBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_cache.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include <nginx.h>\n#include <ngx_md5.h>\n#include \"ngx_http_lua_common.h\"\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_clfactory.h\"\n#include \"ngx_http_lua_util.h\"\n\n\nstatic u_char *ngx_http_lua_gen_file_cache_key_helper(u_char *out,\n    const u_char *src, size_t src_len);\n\n\n/**\n * Find code chunk associated with the given key in code cache,\n * and push it to the top of Lua stack if found.\n *\n * Stack layout before call:\n *         |     ...    | <- top\n *\n * Stack layout after call:\n *         | code chunk | <- top\n *         |     ...    |\n *\n * */\nstatic ngx_int_t\nngx_http_lua_cache_load_code(ngx_log_t *log, lua_State *L,\n    int *ref, const char *key)\n{\n#ifndef OPENRESTY_LUAJIT\n    int          rc;\n    u_char      *err;\n#endif\n\n    /*  get code cache table */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          code_cache_key));\n    lua_rawget(L, LUA_REGISTRYINDEX);    /*  sp++ */\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"code cache lookup (key='%s', ref=%d)\", key, *ref);\n\n    dd(\"code cache table to load: %p\", lua_topointer(L, -1));\n\n    if (!lua_istable(L, -1)) {\n        dd(\"Error: code cache table to load did not exist!!\");\n        return NGX_ERROR;\n    }\n\n    ngx_http_lua_assert(key != NULL);\n\n    if (*ref == LUA_NOREF) {\n        lua_getfield(L, -1, key); /* cache closure */\n\n    } else {\n        if (*ref == LUA_REFNIL) {\n            lua_getfield(L, -1, key); /* cache ref */\n\n            if (!lua_isnumber(L, -1)) {\n                goto not_found;\n            }\n\n            *ref = lua_tonumber(L, -1);\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                           \"code cache setting ref (key='%s', ref=%d)\",\n                           key, *ref);\n\n            lua_pop(L, 1); /* cache */\n        }\n\n        lua_rawgeti(L, -1, *ref); /* cache closure */\n    }\n\n    if (lua_isfunction(L, -1)) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                       \"code cache hit (key='%s', ref=%d)\", key, *ref);\n\n#ifdef OPENRESTY_LUAJIT\n        lua_remove(L, -2);   /*  sp-- */\n        return NGX_OK;\n#else\n        /*  call closure factory to gen new closure */\n        rc = lua_pcall(L, 0, 1, 0);\n        if (rc == 0) {\n            /*  remove cache table from stack, leave code chunk at\n             *  top of stack */\n            lua_remove(L, -2);   /*  sp-- */\n            return NGX_OK;\n        }\n\n        if (lua_isstring(L, -1)) {\n            err = (u_char *) lua_tostring(L, -1);\n\n        } else {\n            err = (u_char *) \"unknown error\";\n        }\n\n        ngx_log_error(NGX_LOG_ERR, log, 0,\n                      \"lua: failed to run factory at key \\\"%s\\\": %s\",\n                      key, err);\n        lua_pop(L, 2);\n        return NGX_ERROR;\n#endif /* OPENRESTY_LUAJIT */\n    }\n\nnot_found:\n\n    dd(\"Value associated with given key in code cache table is not code \"\n       \"chunk: stack top=%d, top value type=%s\\n\",\n       lua_gettop(L), luaL_typename(L, -1));\n\n    /*  remove cache table and value from stack */\n    lua_pop(L, 2);                                /*  sp-=2 */\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"code cache miss (key='%s', ref=%d)\", key, *ref);\n\n    return NGX_DECLINED;\n}\n\n\n/**\n * Store the closure factory at the top of Lua stack to code cache, and\n * associate it with the given key. Then generate new closure.\n *\n * Stack layout before call:\n *         | code factory | <- top\n *         |     ...      |\n *\n * Stack layout after call:\n *         | code chunk | <- top\n *         |     ...    |\n *\n * */\nstatic ngx_int_t\nngx_http_lua_cache_store_code(lua_State *L, int *ref, const char *key)\n{\n#ifndef OPENRESTY_LUAJIT\n    int rc;\n#endif\n\n    /*  get code cache table */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          code_cache_key));\n    lua_rawget(L, LUA_REGISTRYINDEX);\n\n    dd(\"Code cache table to store: %p\", lua_topointer(L, -1));\n\n    if (!lua_istable(L, -1)) {\n        dd(\"Error: code cache table to load did not exist!!\");\n        return NGX_ERROR;\n    }\n\n    ngx_http_lua_assert(key != NULL);\n\n    lua_pushvalue(L, -2); /* closure cache closure */\n\n    if (*ref == LUA_NOREF) {\n        /*  cache closure by cache key */\n        lua_setfield(L, -2, key); /* closure cache */\n\n    } else {\n        /*  cache closure with reference */\n        *ref = luaL_ref(L, -2); /* closure cache */\n\n        /*  cache reference by cache key */\n        lua_pushnumber(L, *ref); /* closure cache ref */\n        lua_setfield(L, -2, key); /* closure cache */\n    }\n\n    /*  remove cache table, leave closure factory at top of stack */\n    lua_pop(L, 1); /* closure */\n\n#ifndef OPENRESTY_LUAJIT\n    /*  call closure factory to generate new closure */\n    rc = lua_pcall(L, 0, 1, 0);\n    if (rc != 0) {\n        dd(\"Error: failed to call closure factory!!\");\n        return NGX_ERROR;\n    }\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_lua_cache_loadbuffer(ngx_log_t *log, lua_State *L,\n    const u_char *src, size_t src_len, int *cache_ref, const u_char *cache_key,\n    const char *name)\n{\n    int          n;\n    ngx_int_t    rc;\n    const char  *err = NULL;\n\n    n = lua_gettop(L);\n\n    rc = ngx_http_lua_cache_load_code(log, L, cache_ref, (char *) cache_key);\n    if (rc == NGX_OK) {\n        return NGX_OK;\n    }\n\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    /* rc == NGX_DECLINED */\n\n    /* load closure factory of inline script to the top of lua stack, sp++ */\n    rc = ngx_http_lua_clfactory_loadbuffer(L, (char *) src, src_len, name);\n\n    if (rc != 0) {\n        /*  Oops! error occurred when loading Lua script */\n        if (rc == LUA_ERRMEM) {\n            err = \"memory allocation error\";\n\n        } else {\n            if (lua_isstring(L, -1)) {\n                err = lua_tostring(L, -1);\n\n            } else {\n                err = \"unknown error\";\n            }\n        }\n\n        goto error;\n    }\n\n    /*  store closure factory and gen new closure at the top of lua stack to\n     *  code cache */\n    rc = ngx_http_lua_cache_store_code(L, cache_ref, (char *) cache_key);\n    if (rc != NGX_OK) {\n        err = \"fail to generate new closure from the closure factory\";\n        goto error;\n    }\n\n    return NGX_OK;\n\nerror:\n\n    ngx_log_error(NGX_LOG_ERR, log, 0,\n                  \"failed to load inlined Lua code: %s\", err);\n    lua_settop(L, n);\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_http_lua_cache_loadfile(ngx_log_t *log, lua_State *L,\n    const u_char *script, int *cache_ref, const u_char *cache_key)\n{\n    int              n;\n    ngx_int_t        rc, errcode = NGX_ERROR;\n    u_char           buf[NGX_HTTP_LUA_FILE_KEY_LEN + 1];\n    const char      *err = NULL;\n\n    n = lua_gettop(L);\n\n    /*  calculate digest of script file path */\n    if (cache_key == NULL) {\n        dd(\"CACHE file key not pre-calculated...calculating\");\n\n        cache_key = ngx_http_lua_gen_file_cache_key_helper(buf, script,\n                                                           ngx_strlen(script));\n        *cache_ref = LUA_NOREF;\n\n    } else {\n        dd(\"CACHE file key already pre-calculated\");\n\n        ngx_http_lua_assert(cache_ref != NULL && *cache_ref != LUA_NOREF);\n    }\n\n    rc = ngx_http_lua_cache_load_code(log, L, cache_ref, (char *) cache_key);\n    if (rc == NGX_OK) {\n        return NGX_OK;\n    }\n\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    /* rc == NGX_DECLINED */\n\n    /*  load closure factory of script file to the top of lua stack, sp++ */\n    rc = ngx_http_lua_clfactory_loadfile(L, (char *) script);\n\n    dd(\"loadfile returns %d (%d)\", (int) rc, LUA_ERRFILE);\n\n    if (rc != 0) {\n        /*  Oops! error occurred when loading Lua script */\n        switch (rc) {\n        case LUA_ERRMEM:\n            err = \"memory allocation error\";\n            break;\n\n        case LUA_ERRFILE:\n            if (errno == ENOENT) {\n                errcode = NGX_HTTP_NOT_FOUND;\n\n            } else {\n                errcode = NGX_HTTP_SERVICE_UNAVAILABLE;\n            }\n\n            /* fall through */\n\n        default:\n            if (lua_isstring(L, -1)) {\n                err = lua_tostring(L, -1);\n\n            } else {\n                err = \"unknown error\";\n            }\n        }\n\n        goto error;\n    }\n\n    /*  store closure factory and gen new closure at the top of lua stack\n     *  to code cache */\n    rc = ngx_http_lua_cache_store_code(L, cache_ref, (char *) cache_key);\n    if (rc != NGX_OK) {\n        err = \"fail to generate new closure from the closure factory\";\n        goto error;\n    }\n\n    return NGX_OK;\n\nerror:\n\n    ngx_log_error(NGX_LOG_ERR, log, 0,\n                  \"failed to load external Lua file \\\"%s\\\": %s\", script, err);\n\n    lua_settop(L, n);\n    return errcode;\n}\n\n\nu_char *\nngx_http_lua_gen_chunk_cache_key(ngx_conf_t *cf, const char *tag,\n    const u_char *src, size_t src_len)\n{\n    u_char      *p, *out;\n    size_t       tag_len;\n\n    tag_len = ngx_strlen(tag);\n\n    out = ngx_palloc(cf->pool, tag_len + NGX_HTTP_LUA_INLINE_KEY_LEN + 2);\n    if (out == NULL) {\n        return NULL;\n    }\n\n    p = ngx_copy(out, tag, tag_len);\n    p = ngx_copy(p, \"_\", 1);\n    p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN);\n    p = ngx_http_lua_digest_hex(p, src, src_len);\n    *p = '\\0';\n\n    return out;\n}\n\n\nstatic u_char *\nngx_http_lua_gen_file_cache_key_helper(u_char *out, const u_char *src,\n    size_t src_len)\n{\n    u_char      *p;\n\n    ngx_http_lua_assert(out != NULL);\n\n    if (out == NULL) {\n        return NULL;\n    }\n\n    p = ngx_copy(out, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN);\n    p = ngx_http_lua_digest_hex(p, src, src_len);\n    *p = '\\0';\n\n    return out;\n}\n\n\nu_char *\nngx_http_lua_gen_file_cache_key(ngx_conf_t *cf, const u_char *src,\n    size_t src_len)\n{\n    u_char      *out;\n\n    out = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1);\n    if (out == NULL) {\n        return NULL;\n    }\n\n    return ngx_http_lua_gen_file_cache_key_helper(out, src, src_len);\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_cache.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_CACHE_H_INCLUDED_\n#define _NGX_HTTP_LUA_CACHE_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_cache_loadbuffer(ngx_log_t *log, lua_State *L,\n    const u_char *src, size_t src_len, int *cache_ref, const u_char *cache_key,\n    const char *name);\nngx_int_t ngx_http_lua_cache_loadfile(ngx_log_t *log, lua_State *L,\n    const u_char *script, int *cache_ref, const u_char *cache_key);\nu_char *ngx_http_lua_gen_chunk_cache_key(ngx_conf_t *cf, const char *tag,\n    const u_char *src, size_t src_len);\nu_char *ngx_http_lua_gen_file_cache_key(ngx_conf_t *cf, const u_char *src,\n    size_t src_len);\n\n\n#endif /* _NGX_HTTP_LUA_CACHE_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_capturefilter.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include <nginx.h>\n#include \"ngx_http_lua_capturefilter.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_exception.h\"\n#include \"ngx_http_lua_subrequest.h\"\n\n\nngx_http_output_header_filter_pt ngx_http_lua_next_header_filter;\nngx_http_output_body_filter_pt ngx_http_lua_next_body_filter;\n\n\nstatic ngx_int_t ngx_http_lua_capture_header_filter(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_lua_capture_body_filter(ngx_http_request_t *r,\n    ngx_chain_t *in);\n\n\nngx_int_t\nngx_http_lua_capture_filter_init(ngx_conf_t *cf)\n{\n    /* setting up output filters to intercept subrequest responses */\n    ngx_http_lua_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_lua_capture_header_filter;\n\n    ngx_http_lua_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_lua_capture_body_filter;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_capture_header_filter(ngx_http_request_t *r)\n{\n    ngx_http_post_subrequest_t      *psr;\n    ngx_http_lua_ctx_t              *old_ctx;\n    ngx_http_lua_ctx_t              *ctx;\n\n    ngx_http_lua_post_subrequest_data_t      *psr_data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua capture header filter, uri \\\"%V\\\"\", &r->uri);\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    dd(\"old ctx: %p\", ctx);\n\n    if (ctx == NULL || ! ctx->capture) {\n\n        psr = r->post_subrequest;\n\n        if (psr != NULL\n            && psr->handler == ngx_http_lua_post_subrequest\n            && psr->data != NULL)\n        {\n            /* the lua ctx has been cleared by ngx_http_internal_redirect,\n             * resume it from the post_subrequest data\n             */\n            psr_data = psr->data;\n\n            old_ctx = psr_data->ctx;\n\n            if (ctx == NULL) {\n                ctx = old_ctx;\n                ngx_http_set_ctx(r, ctx, ngx_http_lua_module);\n\n            } else {\n                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"lua restoring ctx with capture %d, index %d\",\n                               old_ctx->capture, old_ctx->index);\n\n                ctx->capture = old_ctx->capture;\n                ctx->index = old_ctx->index;\n                ctx->body = NULL;\n                ctx->last_body = &ctx->body;\n                psr_data->ctx = ctx;\n            }\n        }\n    }\n\n    if (ctx && ctx->capture) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua capturing response body\");\n\n        /* force subrequest response body buffer in memory */\n        r->filter_need_in_memory = 1;\n        r->header_sent = 1;\n        ctx->header_sent = 1;\n\n        if (r->method == NGX_HTTP_HEAD) {\n            r->header_only = 1;\n        }\n\n        return NGX_OK;\n    }\n\n    return ngx_http_lua_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_capture_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    int                              rc;\n    ngx_int_t                        eof;\n    ngx_http_lua_ctx_t              *ctx;\n    ngx_http_lua_ctx_t              *pr_ctx;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua capture body filter, uri \\\"%V\\\"\", &r->uri);\n\n    if (in == NULL) {\n        return ngx_http_lua_next_body_filter(r, NULL);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (!ctx || !ctx->capture) {\n        dd(\"no ctx or no capture %.*s\", (int) r->uri.len, r->uri.data);\n\n        return ngx_http_lua_next_body_filter(r, in);\n    }\n\n    if (ctx->run_post_subrequest) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua body filter skipped because post subrequest \"\n                       \"already run\");\n        return NGX_OK;\n    }\n\n    if (r->parent == NULL) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua body filter skipped because no parent request \"\n                       \"found\");\n\n        return NGX_ERROR;\n    }\n\n    pr_ctx = ngx_http_get_module_ctx(r->parent, ngx_http_lua_module);\n    if (pr_ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua capture body filter capturing response body, uri \"\n                   \"\\\"%V\\\"\", &r->uri);\n\n    rc = ngx_http_lua_add_copy_chain(r, pr_ctx, &ctx->last_body, in, &eof);\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    dd(\"add copy chain eof: %d, sr: %d\", (int) eof, r != r->main);\n\n    if (eof) {\n        ctx->seen_last_for_subreq = 1;\n    }\n\n    ngx_http_lua_discard_bufs(r->pool, in);\n\n    return NGX_OK;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_capturefilter.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_CAPTUREFILTER_H_INCLUDED_\n#define _NGX_HTTP_LUA_CAPTUREFILTER_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_capture_filter_init(ngx_conf_t *cf);\n\n\n#endif /* NGX_HTTP_LUA_CAPTUREFILTER_H */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_clfactory.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include <nginx.h>\n#include \"ngx_http_lua_clfactory.h\"\n\n\n#ifndef OPENRESTY_LUAJIT\n#define CLFACTORY_BEGIN_CODE \"return function() \"\n#define CLFACTORY_BEGIN_SIZE (sizeof(CLFACTORY_BEGIN_CODE) - 1)\n\n#define CLFACTORY_END_CODE \"\\nend\"\n#define CLFACTORY_END_SIZE (sizeof(CLFACTORY_END_CODE) - 1)\n#endif\n\n\n/*\n * taken from chaoslawful:\n * Lua bytecode header        Luajit bytecode header\n * --------------              --------------\n * |  \\033Lua   | 0-3          |  \\033LJ    | 0-2\n * --------------              --------------\n * |    LuaC    | 4            |  bytecode  | 3\n * |   Version  |              |   version  |\n * --------------              --------------\n * |    LuaC    | 5            |  misc flag | 4 [F|S|B]\n * |   Format   |              --------------\n * --------------              |  chunkname | ULEB128 var-len\n * |   Endian   | 6            |     len    | encoded uint32\n * --------------              --------------\n * |   size of  | 7            |  chunkname |\n * |     int    |              |  str no \\0 |\n * --------------              --------------\n * |   size of  | 8\n * |    size_t  |\n * --------------\n * |   size of  | 9\n * | instruction|\n * --------------\n * |   size of  | 10\n * |   number   |\n * --------------\n * |   number   | 11\n * |   is int?  |\n * --------------\n*/\n\n\n/*\n * CLOSURE 0 0 RETURN 0 2 RETURN 0 1\n * length(Instruction) = 4 or 8\n * little endian or big endian\n*/\n#ifndef OPENRESTY_LUAJIT\n#define    LUA_LITTLE_ENDIAN_4BYTES_CODE                                     \\\n    \"\\x24\\x00\\x00\\x00\\x1e\\x00\\x00\\x01\\x1e\\x00\\x80\\x00\"\n#define    LUA_LITTLE_ENDIAN_8BYTES_CODE                                     \\\n    \"\\x24\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1e\\x00\\x00\\x01\"                       \\\n    \"\\x00\\x00\\x00\\x00\\x1e\\x00\\x80\\x00\\x00\\x00\\x00\\x00\"\n#define    LUA_BIG_ENDIAN_4BYTES_CODE                                        \\\n    \"\\x00\\x00\\x00\\x24\\x01\\x00\\x00\\x1e\\x00\\x08\\x00\\x1e\"\n#define    LUA_BIG_ENDIAN_8BYTES_CODE                                        \\\n    \"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\"                       \\\n    \"\\x01\\x00\\x00\\x1e\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x1e\"\n#define    LUA_LITTLE_ENDIAN_4BYTES_CODE_LEN        (4 + 4 + 4)\n#define    LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN        (8 + 8 + 8)\n#define    LUA_BIG_ENDIAN_4BYTES_CODE_LEN           (4 + 4 + 4)\n#define    LUA_BIG_ENDIAN_8BYTES_CODE_LEN           (8 + 8 + 8)\n#define    LUAC_HEADERSIZE         12\n#define    LUAC_VERSION            0x51\n#endif /* OPENRESTY_LUAJIT */\n\n\n/*\n * taken from chaoslawful:\n *  Lua Proto\n * ---------------------\n * | String            | Can be empty string\n * | [source]          | (stripped or internal function)\n * ---------------------\n * | Int               | At which line this function is defined\n * | [linedefined]     |\n * ---------------------\n * | Int               | At which line this function definition ended\n * | [lastlinedefined] |\n * ---------------------\n * | Char              | Number of upvalues referenced by this function\n * | [nups]            |\n * ---------------------\n * | Char              | Number of parameters of this function\n * | [numparams]       |\n * ---------------------\n * | Char              | Does this function has variable number of arguments?\n * | [is_var_arg]      | main function always set to VARARG_ISVARARG (2)\n * ---------------------\n * | Char              | Maximum stack size this function used\n * | [maxstacksize]    | Initially set to 2\n * ---------------------\n * | Vector(instr)     | Code instructions of this function\n * | [code]            |\n * ---------------------\n * | Int               | Number of constants referenced by this function\n * | [sizek]           |\n * ---------------------\n * | Char              | ------------------------------------\n * | type of [k[i]]    |  The type and content of constants |\n * ---------------------                                    |-> repeat for i in\n * | Char if boolean   |  No content part if type is NIL    |   [1..sizek]\n * | Number if number  | ------------------------------------\n * | String if string  |\n * ---------------------\n * | Int               | Number of internal functions\n * | [sizep]           |\n * ---------------------\n * | Function          | -> repeat for i in [1..sizep]\n * | at [p[i]]         |\n * ---------------------\n * | Vector            | Debug lineinfo vector\n * | [lineinfo]        | Empty vector here if debug info is stripped\n * ---------------------\n * | Int               | Number of local variables in this function\n * | [sizelocvars]     | 0 if debug info is stripped\n * ---------------------\n * | String            | ------------------------------------\n * | [locvars[i]]      |  Name of local var i               |\n * |  .varname]        |                                    |\n * ---------------------                                    |\n * | Int               |  instruction counter               |\n * | [locvars[i]]      |  where local var i start to be     |-> repeat for i in\n * |  .startpc]        |  referenced                        |  [0..sizelocvars]\n * ---------------------                                    |\n * | Int               |  instruction counter, where local  |\n * | [locvars[i]]      |  var i ceased to be referenced     |\n * |  .endpc]          | ------------------------------------\n * ---------------------\n * | Int               | Number of upvalues referenced by this function,\n * | [sizeupvalues]    | 0 if stripped\n * ---------------------\n * | String            | -> repeat for i in[0..sizeupvalues]\n * | [upvalues[i]]     |\n * ---------------------\n*/\n\n#ifndef OPENRESTY_LUAJIT\n#define    POS_SOURCE_STR_LEN      LUAC_HEADERSIZE\n#define    POS_START_LINE          (POS_SOURCE_STR_LEN + sizeof(size_t))\n#define    POS_LAST_LINE           (POS_START_LINE + sizeof(int))\n#define    POS_NUM_OF_UPVS         (POS_LAST_LINE + sizeof(int))\n#define    POS_NUM_OF_PARA         (POS_NUM_OF_UPVS + sizeof(char))\n#define    POS_IS_VAR_ARG          (POS_NUM_OF_PARA + sizeof(char))\n#define    POS_MAX_STACK_SIZE      (POS_IS_VAR_ARG + sizeof(char))\n#define    POS_NUM_OF_INST         (POS_MAX_STACK_SIZE +sizeof(char))\n#define    POS_BYTECODE            (POS_NUM_OF_INST + sizeof(int))\n#define    MAX_BEGIN_CODE_SIZE                                               \\\n    (POS_BYTECODE + LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN                        \\\n    + sizeof(int) + sizeof(int))\n#define    MAX_END_CODE_SIZE       (sizeof(int) + sizeof(int) + sizeof(int))\n#endif /* OPENRESTY_LUAJIT */\n\n/*\n * taken from chaoslawful:\n * Luajit bytecode format\n * ---------------------\n * | HEAD              | Luajit bytecode head\n * ---------------------\n * | Internal          | All internal functions\n * | functions         |\n * ---------------------\n * | ULEB128           | Rest data total length of this function\n * | [Date len of      | (not include itself)\n * |  this function]   |\n * ---------------------\n * | Char              | F(ffi) | V(vararg)| C(has internal funcs)\n * | [func flag]       |\n * ---------------------\n * | Char              | Number of parameters of this function\n * | [numparams]       |\n * ---------------------\n * | Char              |\n * | [framesize]       |\n * ---------------------\n * | Char              | Number of upvalues referenced by this function\n * | [sizeupvalues]    |\n * ---------------------\n * | ULEB128           | Number of collectable constants referenced\n * | [sizekgc]         | by this function\n * ---------------------\n * | ULEB128           | Number of lua number constants referenced\n * | [sizekn]          | by this function\n * ---------------------\n * | ULEB128           | Number of bytecode instructions of this function\n * | [sizebc]m1        | minus 1 to omit the BC_FUNCV/BC_FUNCF header bytecode\n * ---------------------\n * | ULEB128           |\n * | [size of dbg      | Size of debug lineinfo map, available when not stripped\n * |  lineinfo]        |\n * ---------------------\n * | ULEB128           | Available when not stripped\n * | [firstline]       | The first line of this function's definition\n * ---------------------\n * | ULEB128           | Available when not stripped\n * | [numline]         | The number of lines of this function's definition\n * ---------------------\n * | [bytecode]        | Bytecode instructions of this function\n * ---------------------\n * |[upvalue ref slots]| [sizeupvalues] * 2\n * ---------------------\n * | [collectable      | [sizekgc] elems, variable length\n * |  constants]       |\n * ---------------------\n * | [lua number       | [sizekn] elems, variable length\n * |  constants]       |\n * ---------------------\n * | [debug lineinfo   | Length is the calculated size of debug lineinfo above\n * |                   | Only available if not stripped\n * ---------------------\n * | Char              |\n * | [\\x00]            | Footer\n * ---------------------\n*/\n\n/* bytecode for luajit 2.0 */\n\n#ifndef OPENRESTY_LUAJIT\n#define    LJ20_LITTLE_ENDIAN_CODE_STRIPPED                                  \\\n    \"\\x14\\x03\\x00\\x01\\x00\\x01\\x00\\x03\"                                       \\\n    \"\\x31\\x00\\x00\\x00\\x30\\x00\\x00\\x80\\x48\\x00\\x02\\x00\"                       \\\n    \"\\x00\\x00\"\n\n#define    LJ20_BIG_ENDIAN_CODE_STRIPPED                                     \\\n    \"\\x14\\x03\\x00\\x01\\x00\\x01\\x00\\x03\"                                       \\\n    \"\\x00\\x00\\x00\\x31\\x80\\x00\\x00\\x30\\x00\\x02\\x00\\x48\"                       \\\n    \"\\x00\\x00\"\n\n#define    LJ20_LITTLE_ENDIAN_CODE                                           \\\n    \"\\x15\\x03\\x00\\x01\\x00\\x01\\x00\\x03\\x00\"                                   \\\n    \"\\x31\\x00\\x00\\x00\\x30\\x00\\x00\\x80\\x48\\x00\\x02\\x00\"                       \\\n    \"\\x00\\x00\"\n\n#define    LJ20_BIG_ENDIAN_CODE                                              \\\n    \"\\x15\\x03\\x00\\x01\\x00\\x01\\x00\\x03\\x00\"                                   \\\n    \"\\x00\\x00\\x00\\x31\\x80\\x00\\x00\\x30\\x00\\x02\\x00\\x48\"                       \\\n    \"\\x00\\x00\"\n\n/* bytecode for luajit 2.1 */\n\n#define    LJ21_LITTLE_ENDIAN_CODE_STRIPPED                                  \\\n    \"\\x14\\x03\\x00\\x01\\x00\\x01\\x00\\x03\"                                       \\\n    \"\\x33\\x00\\x00\\x00\\x32\\x00\\x00\\x80\\x4c\\x00\\x02\\x00\"                       \\\n    \"\\x00\\x00\"\n\n#define    LJ21_BIG_ENDIAN_CODE_STRIPPED                                     \\\n    \"\\x14\\x03\\x00\\x01\\x00\\x01\\x00\\x03\"                                       \\\n    \"\\x00\\x00\\x00\\x33\\x80\\x00\\x00\\x32\\x00\\x02\\x00\\x4c\"                       \\\n    \"\\x00\\x00\"\n\n#define    LJ21_LITTLE_ENDIAN_CODE                                           \\\n    \"\\x15\\x03\\x00\\x01\\x00\\x01\\x00\\x03\\x00\"                                   \\\n    \"\\x33\\x00\\x00\\x00\\x32\\x00\\x00\\x80\\x4c\\x00\\x02\\x00\"                       \\\n    \"\\x00\\x00\"\n\n#define    LJ21_BIG_ENDIAN_CODE                                              \\\n    \"\\x15\\x03\\x00\\x01\\x00\\x01\\x00\\x03\\x00\"                                   \\\n    \"\\x00\\x00\\x00\\x33\\x80\\x00\\x00\\x32\\x00\\x02\\x00\\x4c\"                       \\\n    \"\\x00\\x00\"\n\n#define    LJ_CODE_LEN              23\n#define    LJ_CODE_LEN_STRIPPED     22\n#define    LJ_HEADERSIZE            5\n#define    LJ_BCDUMP_F_BE           0x01\n#define    LJ_BCDUMP_F_STRIP        0x02\n#define    LJ21_BCDUMP_VERSION        2\n#define    LJ20_BCDUMP_VERSION        1\n#define    LJ_SIGNATURE             \"\\x1b\\x4c\\x4a\"\n#endif /* OPENRESTY_LUAJIT */\n\n\ntypedef enum {\n    NGX_LUA_TEXT_FILE,\n    NGX_LUA_BT_LUA,\n    NGX_LUA_BT_LJ,\n} ngx_http_lua_clfactory_file_type_e;\n\n\nenum {\n    NGX_LUA_READER_BUFSIZE = 4096,\n};\n\n\ntypedef struct {\n    ngx_http_lua_clfactory_file_type_e file_type;\n\n    int         extraline;\n    FILE       *f;\n#ifndef OPENRESTY_LUAJIT\n    int         sent_begin;\n    int         sent_end;\n    size_t      begin_code_len;\n    size_t      end_code_len;\n    size_t      rest_len;\n    union {\n        char   *ptr;\n        char    str[MAX_BEGIN_CODE_SIZE];\n    }           begin_code;\n    union {\n        char   *ptr;\n        char    str[MAX_END_CODE_SIZE];\n    }           end_code;\n#endif /* OPENRESTY_LUAJIT */\n    char        buff[NGX_LUA_READER_BUFSIZE];\n} ngx_http_lua_clfactory_file_ctx_t;\n\n\ntypedef struct {\n#ifndef OPENRESTY_LUAJIT\n    int         sent_begin;\n    int         sent_end;\n#endif\n    const char *s;\n    size_t      size;\n} ngx_http_lua_clfactory_buffer_ctx_t;\n\n\nstatic const char *ngx_http_lua_clfactory_getF(lua_State *L, void *ud,\n    size_t *size);\nstatic int ngx_http_lua_clfactory_errfile(lua_State *L, const char *what,\n    int fname_index);\nstatic const char *ngx_http_lua_clfactory_getS(lua_State *L, void *ud,\n    size_t *size);\n#ifndef OPENRESTY_LUAJIT\nstatic long ngx_http_lua_clfactory_file_size(FILE *f);\n#endif\n\n\n#ifndef OPENRESTY_LUAJIT\nint\nngx_http_lua_clfactory_bytecode_prepare(lua_State *L,\n    ngx_http_lua_clfactory_file_ctx_t *lf, int fname_index)\n{\n    int                 x = 1, size_of_int, size_of_size_t, little_endian,\n                        size_of_inst, version, stripped;\n    static int          num_of_inst = 3, num_of_inter_func = 1;\n    const char         *emsg, *serr, *bytecode;\n    size_t              size, bytecode_len;\n    long                fsize;\n\n    serr = NULL;\n\n    *lf->begin_code.str = LUA_SIGNATURE[0];\n\n    if (lf->file_type == NGX_LUA_BT_LJ) {\n        size = fread(lf->begin_code.str + 1, 1, LJ_HEADERSIZE - 1, lf->f);\n\n        if (size != LJ_HEADERSIZE - 1) {\n            serr = strerror(errno);\n            emsg = \"cannot read header\";\n            goto error;\n        }\n\n        version = *(lf->begin_code.str + 3);\n\n        dd(\"version: %d\", (int) version);\n\n        if (ngx_memcmp(lf->begin_code.str, LJ_SIGNATURE,\n                       sizeof(LJ_SIGNATURE) - 1))\n        {\n            emsg = \"bad byte-code header\";\n            goto error;\n        }\n\n#if defined(DDEBUG) && (DDEBUG)\n        {\n            dd(\"==LJ_BT_HEADER==\");\n            size_t i;\n            for (i = 0; i < LJ_HEADERSIZE; i++) {\n                dd(\"%ld: 0x%02X\", i, (unsigned)(u_char) lf->begin_code.str[i]);\n            }\n            dd(\"==LJ_BT_HEADER_END==\");\n        }\n#endif\n\n        lf->begin_code_len = LJ_HEADERSIZE;\n        little_endian = !((*(lf->begin_code.str + 4)) & LJ_BCDUMP_F_BE);\n        stripped = (*(lf->begin_code.str + 4)) & LJ_BCDUMP_F_STRIP;\n\n        dd(\"stripped: %d\", (int) stripped);\n\n        if (version == LJ21_BCDUMP_VERSION) {\n            if (stripped) {\n                if (little_endian) {\n                    lf->end_code.ptr = LJ21_LITTLE_ENDIAN_CODE_STRIPPED;\n\n                } else {\n                    lf->end_code.ptr = LJ21_BIG_ENDIAN_CODE_STRIPPED;\n                }\n\n                lf->end_code_len = LJ_CODE_LEN_STRIPPED;\n\n            } else {\n                if (little_endian) {\n                    lf->end_code.ptr = LJ21_LITTLE_ENDIAN_CODE;\n\n                } else {\n                    lf->end_code.ptr = LJ21_BIG_ENDIAN_CODE;\n                }\n\n                lf->end_code_len = LJ_CODE_LEN;\n            }\n\n        } else if (version == LJ20_BCDUMP_VERSION) {\n            if (stripped) {\n                if (little_endian) {\n                    lf->end_code.ptr = LJ20_LITTLE_ENDIAN_CODE_STRIPPED;\n\n                } else {\n                    lf->end_code.ptr = LJ20_BIG_ENDIAN_CODE_STRIPPED;\n                }\n\n                lf->end_code_len = LJ_CODE_LEN_STRIPPED;\n\n            } else {\n                if (little_endian) {\n                    lf->end_code.ptr = LJ20_LITTLE_ENDIAN_CODE;\n\n                } else {\n                    lf->end_code.ptr = LJ20_BIG_ENDIAN_CODE;\n                }\n\n                lf->end_code_len = LJ_CODE_LEN;\n            }\n\n        } else {\n            emsg = \"bytecode format version unsupported\";\n            goto error;\n        }\n\n        fsize = ngx_http_lua_clfactory_file_size(lf->f);\n        if (fsize < 0) {\n            serr = strerror(errno);\n            emsg = \"cannot fseek/ftell\";\n            goto error;\n        }\n\n        lf->rest_len = fsize - LJ_HEADERSIZE;\n\n#if defined(DDEBUG) && (DDEBUG)\n        {\n        size_t i = 0;\n        dd(\"==LJ_END_CODE: %ld rest_len: %ld==\", lf->end_code_len,\n           lf->rest_len);\n\n        for (i = 0; i < lf->end_code_len; i++) {\n            dd(\"%ld: 0x%02X\", i, (unsigned) ((u_char) lf->end_code.ptr[i]));\n        }\n        dd(\"==LJ_END_CODE_END==\");\n        }\n#endif\n\n    } else {\n        size = fread(lf->begin_code.str + 1, 1, LUAC_HEADERSIZE - 1, lf->f);\n\n        if (size != LUAC_HEADERSIZE - 1) {\n            serr = strerror(errno);\n            emsg = \"cannot read header\";\n            goto error;\n        }\n\n        version = *(lf->begin_code.str + 4);\n        little_endian = *(lf->begin_code.str + 6);\n        size_of_int = *(lf->begin_code.str + 7);\n        size_of_size_t = *(lf->begin_code.str + 8);\n        size_of_inst = *(lf->begin_code.str + 9);\n\n#if defined(DDEBUG) && (DDEBUG)\n        {\n        dd(\"==LUA_BT_HEADER==\");\n        size_t i;\n        for (i = 0; i < LUAC_HEADERSIZE; i++) {\n            dd(\"%ld, 0x%02X\", i, (unsigned)(u_char) lf->begin_code.str[i]);\n        }\n        dd(\"==LUA_BT_HEADER_END==\");\n        }\n#endif\n\n        if (ngx_memcmp(lf->begin_code.str, LUA_SIGNATURE,\n                       sizeof(LUA_SIGNATURE) -1)\n            || version != LUAC_VERSION\n            || little_endian != (int) (*(char *) &x)\n            || size_of_int != sizeof(int)\n            || size_of_size_t != sizeof(size_t)\n            || (size_of_inst != 4 && size_of_inst != 8))\n        {\n            emsg = \"bad byte-code header\";\n            goto error;\n        }\n\n        /* clear the following fields to zero:\n         * - source string length\n         * - start line\n         * - last line\n         */\n        ngx_memzero(lf->begin_code.str + POS_SOURCE_STR_LEN,\n                    sizeof(size_t) + sizeof(int) * 2);\n        /* number of upvalues */\n        *(lf->begin_code.str + POS_NUM_OF_UPVS) = 0;\n        /* number of parameters */\n        *(lf->begin_code.str + POS_NUM_OF_PARA) = 0;\n        /* is var-argument function? */\n        *(lf->begin_code.str + POS_IS_VAR_ARG) = 2;\n        /* max stack size */\n        *(lf->begin_code.str + POS_MAX_STACK_SIZE) = 2;\n        /* number of bytecode instructions */\n        ngx_memcpy(lf->begin_code.str + POS_NUM_OF_INST, &num_of_inst,\n                   sizeof(int));\n\n        lf->begin_code_len = POS_BYTECODE;\n\n        if (little_endian) {\n            if (size_of_inst == 4) {\n                bytecode = LUA_LITTLE_ENDIAN_4BYTES_CODE;\n                bytecode_len = LUA_LITTLE_ENDIAN_4BYTES_CODE_LEN;\n\n            } else {\n                bytecode = LUA_LITTLE_ENDIAN_8BYTES_CODE;\n                bytecode_len = LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN;\n            }\n\n        } else {\n            if (size_of_inst == 4) {\n                bytecode = LUA_BIG_ENDIAN_4BYTES_CODE;\n                bytecode_len = LUA_BIG_ENDIAN_4BYTES_CODE_LEN;\n\n            } else {\n                bytecode = LUA_BIG_ENDIAN_8BYTES_CODE;\n                bytecode_len = LUA_BIG_ENDIAN_8BYTES_CODE_LEN;\n            }\n        }\n\n        /* bytecode */\n        ngx_memcpy(lf->begin_code.str + POS_BYTECODE, bytecode, bytecode_len);\n\n        /* number of consts */\n        ngx_memzero(lf->begin_code.str + POS_BYTECODE + bytecode_len,\n                    sizeof(int));\n        /* number of internal functions */\n        ngx_memcpy(lf->begin_code.str + POS_BYTECODE + bytecode_len\n                   + sizeof(int), &num_of_inter_func, sizeof(int));\n\n        lf->begin_code_len += bytecode_len + sizeof(int) + sizeof(int);\n\n#if defined(DDEBUG) && (DDEBUG)\n        {\n        size_t i = 0;\n        dd(\"==LUA_BEGIN_CODE: %ld==\", lf->begin_code_len);\n        for (i = 0; i < lf->begin_code_len; i++) {\n            dd(\"%ld: 0x%02X\", i, (unsigned) ((u_char) lf->begin_code.str[i]));\n        }\n        dd(\"==LUA_BEGIN_CODE_END==\");\n        }\n#endif\n\n        /* clear the following fields to zero:\n         * - lineinfo vector size\n         * - number of local vars\n         * - number of upvalues\n         */\n        ngx_memzero(lf->end_code.str, sizeof(int) * 3);\n\n        lf->end_code_len = sizeof(int) + sizeof(int) + sizeof(int);\n\n#if defined(DDEBUG) && (DDEBUG)\n        {\n        size_t i = 0;\n        dd(\"==LUA_END_CODE: %ld==\", lf->end_code_len);\n        for (i = 0; i < lf->end_code_len; i++) {\n            dd(\"%ld: 0x%02X\", i, (unsigned) ((u_char) lf->end_code.str[i]));\n        }\n        dd(\"==LUA_END_CODE_END==\");\n        }\n#endif\n\n    }\n\n    return 0;\n\nerror:\n\n    fclose(lf->f);  /* close file (even in case of errors) */\n\n    if (serr) {\n        lua_pushfstring(L, \"%s: %s\", emsg, serr);\n\n    } else {\n        lua_pushstring(L, emsg);\n    }\n\n    lua_remove(L, fname_index);\n\n    return LUA_ERRFILE;\n}\n#endif /* OPENRESTY_LUAJIT */\n\n\nngx_int_t\nngx_http_lua_clfactory_loadfile(lua_State *L, const char *filename)\n{\n    int                         c, status, readstatus;\n    ngx_flag_t                  sharp;\n\n    ngx_http_lua_clfactory_file_ctx_t        lf;\n\n    /* index of filename on the stack */\n    int                         fname_index;\n\n    sharp = 0;\n    fname_index = lua_gettop(L) + 1;\n\n    lf.extraline = 0;\n    lf.file_type = NGX_LUA_TEXT_FILE;\n\n#ifndef OPENRESTY_LUAJIT\n    lf.begin_code.ptr = CLFACTORY_BEGIN_CODE;\n    lf.begin_code_len = CLFACTORY_BEGIN_SIZE;\n    lf.end_code.ptr = CLFACTORY_END_CODE;\n    lf.end_code_len = CLFACTORY_END_SIZE;\n#endif\n\n    lua_pushfstring(L, \"@%s\", filename);\n\n    lf.f = fopen(filename, \"r\");\n    if (lf.f == NULL) {\n        return ngx_http_lua_clfactory_errfile(L, \"open\", fname_index);\n    }\n\n    c = getc(lf.f);\n\n    if (c == '#') {  /* Unix exec. file? */\n        lf.extraline = 1;\n\n        while ((c = getc(lf.f)) != EOF && c != '\\n') {\n            /* skip first line */\n        }\n\n        if (c == '\\n') {\n            c = getc(lf.f);\n        }\n\n        sharp = 1;\n    }\n\n    if (c == LUA_SIGNATURE[0] && filename) {  /* binary file? */\n        lf.f = freopen(filename, \"rb\", lf.f);  /* reopen in binary mode */\n\n        if (lf.f == NULL) {\n            return ngx_http_lua_clfactory_errfile(L, \"reopen\", fname_index);\n        }\n\n        /* check whether lib jit exists */\n        luaL_findtable(L, LUA_REGISTRYINDEX, \"_LOADED\", 1);\n        lua_getfield(L, -1, \"jit\");  /* get _LOADED[\"jit\"] */\n\n        if (lua_istable(L, -1)) {\n            lf.file_type = NGX_LUA_BT_LJ;\n\n        } else {\n            lf.file_type = NGX_LUA_BT_LUA;\n        }\n\n        lua_pop(L, 2);\n\n        /*\n         * Loading bytecode with an extra header is disabled for security\n         * reasons. This may circumvent the usual check for bytecode vs.\n         * Lua code by looking at the first char. Since this is a potential\n         * security violation no attempt is made to echo the chunkname either.\n         */\n        if (lf.file_type == NGX_LUA_BT_LJ && sharp) {\n\n            if (filename) {\n                fclose(lf.f);  /* close file (even in case of errors) */\n            }\n\n            filename = lua_tostring(L, fname_index) + 1;\n            lua_pushfstring(L, \"bad byte-code header in %s\", filename);\n            lua_remove(L, fname_index);\n\n            return LUA_ERRFILE;\n        }\n\n        while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) {\n            /* skip eventual `#!...' */\n        }\n\n#ifndef OPENRESTY_LUAJIT\n        status = ngx_http_lua_clfactory_bytecode_prepare(L, &lf, fname_index);\n\n        if (status != 0) {\n            return status;\n        }\n#endif\n\n        lf.extraline = 0;\n    }\n\n#ifndef OPENRESTY_LUAJIT\n    if (lf.file_type == NGX_LUA_TEXT_FILE) {\n        ungetc(c, lf.f);\n    }\n\n    lf.sent_begin = lf.sent_end = 0;\n\n#else\n    ungetc(c, lf.f);\n#endif\n    status = lua_load(L, ngx_http_lua_clfactory_getF, &lf,\n                      lua_tostring(L, -1));\n\n    readstatus = ferror(lf.f);\n\n    if (filename) {\n        fclose(lf.f);  /* close file (even in case of errors) */\n    }\n\n    if (readstatus) {\n        lua_settop(L, fname_index);  /* ignore results from `lua_load' */\n        return ngx_http_lua_clfactory_errfile(L, \"read\", fname_index);\n    }\n\n    lua_remove(L, fname_index);\n\n    return status;\n}\n\n\nngx_int_t\nngx_http_lua_clfactory_loadbuffer(lua_State *L, const char *buff,\n    size_t size, const char *name)\n{\n    ngx_http_lua_clfactory_buffer_ctx_t     ls;\n\n    ls.s = buff;\n    ls.size = size;\n#ifndef OPENRESTY_LUAJIT\n    ls.sent_begin = 0;\n    ls.sent_end = 0;\n#endif\n\n    return lua_load(L, ngx_http_lua_clfactory_getS, &ls, name);\n}\n\n\nstatic const char *\nngx_http_lua_clfactory_getF(lua_State *L, void *ud, size_t *size)\n{\n#ifndef OPENRESTY_LUAJIT\n    char                        *buf;\n#endif\n    size_t                       num;\n\n    ngx_http_lua_clfactory_file_ctx_t        *lf;\n\n    lf = (ngx_http_lua_clfactory_file_ctx_t *) ud;\n\n    if (lf->extraline) {\n        lf->extraline = 0;\n        *size = 1;\n        return \"\\n\";\n    }\n\n#ifndef OPENRESTY_LUAJIT\n    if (lf->sent_begin == 0) {\n        lf->sent_begin = 1;\n        *size = lf->begin_code_len;\n\n        if (lf->file_type == NGX_LUA_TEXT_FILE) {\n            buf = lf->begin_code.ptr;\n\n        } else {\n            buf = lf->begin_code.str;\n        }\n\n        return buf;\n    }\n#endif /* OPENRESTY_LUAJIT */\n\n    num = fread(lf->buff, 1, sizeof(lf->buff), lf->f);\n\n    dd(\"fread returned %d\", (int) num);\n\n    if (num == 0) {\n#ifndef OPENRESTY_LUAJIT\n        if (lf->sent_end == 0) {\n            lf->sent_end = 1;\n            *size = lf->end_code_len;\n\n            if (lf->file_type == NGX_LUA_BT_LUA) {\n                buf = lf->end_code.str;\n\n            } else {\n                buf = lf->end_code.ptr;\n            }\n\n            return buf;\n        }\n#endif /* OPENRESTY_LUAJIT */\n\n        *size = 0;\n        return NULL;\n    }\n\n#ifndef OPENRESTY_LUAJIT\n    if (lf->file_type == NGX_LUA_BT_LJ) {\n        /* skip the footer(\\x00) in luajit */\n\n        lf->rest_len -= num;\n\n        if (lf->rest_len == 0) {\n            if (--num == 0 && lf->sent_end == 0) {\n                lf->sent_end = 1;\n                buf = lf->end_code.ptr;\n                *size = lf->end_code_len;\n\n                return buf;\n            }\n        }\n    }\n#endif /* OPENRESTY_LUAJIT */\n\n    *size = num;\n    return lf->buff;\n}\n\n\nstatic int\nngx_http_lua_clfactory_errfile(lua_State *L, const char *what, int fname_index)\n{\n    const char      *serr;\n    const char      *filename;\n\n    filename = lua_tostring(L, fname_index) + 1;\n\n    if (errno) {\n        serr = strerror(errno);\n        lua_pushfstring(L, \"cannot %s %s: %s\", what, filename, serr);\n\n    } else {\n        lua_pushfstring(L, \"cannot %s %s\", what, filename);\n    }\n\n    lua_remove(L, fname_index);\n\n    return LUA_ERRFILE;\n}\n\n\nstatic const char *\nngx_http_lua_clfactory_getS(lua_State *L, void *ud, size_t *size)\n{\n    ngx_http_lua_clfactory_buffer_ctx_t      *ls = ud;\n\n#ifndef OPENRESTY_LUAJIT\n    if (ls->sent_begin == 0) {\n        ls->sent_begin = 1;\n        *size = CLFACTORY_BEGIN_SIZE;\n\n        return CLFACTORY_BEGIN_CODE;\n    }\n#endif\n\n    if (ls->size == 0) {\n#ifndef OPENRESTY_LUAJIT\n        if (ls->sent_end == 0) {\n            ls->sent_end = 1;\n            *size = CLFACTORY_END_SIZE;\n            return CLFACTORY_END_CODE;\n        }\n#endif\n\n        return NULL;\n    }\n\n    *size = ls->size;\n    ls->size = 0;\n\n    return ls->s;\n}\n\n\n#ifndef OPENRESTY_LUAJIT\nstatic long\nngx_http_lua_clfactory_file_size(FILE *f)\n{\n    long              cur_pos, len;\n\n    cur_pos = ftell(f);\n    if (cur_pos == -1) {\n        return -1;\n    }\n\n    if (fseek(f, 0, SEEK_END) != 0) {\n        return -1;\n    }\n\n    len = ftell(f);\n    if (len == -1) {\n        return -1;\n    }\n\n    if (fseek(f, cur_pos, SEEK_SET) != 0) {\n        return -1;\n    }\n\n    return len;\n}\n#endif /* OPENRESTY_LUAJIT */\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_clfactory.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_CLFACTORY_H_INCLUDED_\n#define _NGX_HTTP_LUA_CLFACTORY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_clfactory_loadfile(lua_State *L, const char *filename);\nngx_int_t ngx_http_lua_clfactory_loadbuffer(lua_State *L, const char *buff,\n    size_t size, const char *name);\n\n\n#endif /* _NGX_HTTP_LUA_CLFACTORY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_common.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_COMMON_H_INCLUDED_\n#define _NGX_HTTP_LUA_COMMON_H_INCLUDED_\n\n\n#include \"ngx_http_lua_autoconf.h\"\n\n#include <nginx.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_md5.h>\n\n#include <setjmp.h>\n#include <stdint.h>\n\n#include <luajit.h>\n#include <lualib.h>\n#include <lauxlib.h>\n\n\n\n#if (NGX_HTTP_SSL)\n/* introduce OPENSSL_IS_BORINGSSL and LIBRESSL_VERSION_NUMBER */\n#include <openssl/ssl.h>\n\n#ifdef HAVE_PROXY_SSL_PATCH\n\n#if defined(LIBRESSL_VERSION_NUMBER)\n#define HAVE_LUA_PROXY_SSL  0\n#elif defined(OPENSSL_IS_BORINGSSL)\n#define  HAVE_LUA_PROXY_SSL 0\n#elif defined(SSL_ERROR_WANT_RETRY_VERIFY) &&                                \\\n    OPENSSL_VERSION_NUMBER >= 0x30000020uL\n#define  HAVE_LUA_PROXY_SSL 1\n#else\n#define  HAVE_LUA_PROXY_SSL 0\n#endif\n\n#endif /* HAVE_PROXY_SSL_PATCH */\n#endif /* NGX_HTTP_SSL */\n\n\n#if defined(NDK) && NDK\n#include <ndk.h>\n\ntypedef struct {\n    size_t       size;\n    int          ref;\n    u_char      *key;\n    u_char      *chunkname;\n    ngx_str_t    script;\n} ngx_http_lua_set_var_data_t;\n#endif\n\n\n#ifdef NGX_LUA_USE_ASSERT\n#include <assert.h>\n#   define ngx_http_lua_assert(a)  assert(a)\n#else\n#   define ngx_http_lua_assert(a)\n#endif\n\n\n/**\n * max positive +1.7976931348623158e+308\n * min positive +2.2250738585072014e-308\n */\n#ifndef NGX_DOUBLE_LEN\n#define NGX_DOUBLE_LEN  25\n#endif\n\n\n#if (NGX_PCRE)\n#   if (NGX_PCRE2)\n#       define LUA_HAVE_PCRE_JIT 1\n#   else\n\n#include <pcre.h>\n\n#       if (PCRE_MAJOR > 8) || (PCRE_MAJOR == 8 && PCRE_MINOR >= 21)\n#           define LUA_HAVE_PCRE_JIT 1\n#       else\n#           define LUA_HAVE_PCRE_JIT 0\n#       endif\n#   endif\n#endif\n\n\n#if (nginx_version < 1006000)\n#   error at least nginx 1.6.0 is required but found an older version\n#endif\n\n#if LUA_VERSION_NUM != 501\n#   error unsupported Lua language version\n#endif\n\n#if !defined(LUAJIT_VERSION_NUM) || (LUAJIT_VERSION_NUM < 20000)\n#   error unsupported LuaJIT version\n#endif\n\n\n#if (!defined OPENSSL_NO_OCSP && defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB)\n#   define NGX_HTTP_LUA_USE_OCSP 1\n#endif\n\n#ifndef NGX_HTTP_PERMANENT_REDIRECT\n#   define NGX_HTTP_PERMANENT_REDIRECT 308\n#endif\n\n#ifndef NGX_HAVE_SHA1\n#   if (nginx_version >= 1011002)\n#       define NGX_HAVE_SHA1 1\n#   endif\n#endif\n\n#ifndef MD5_DIGEST_LENGTH\n#   define MD5_DIGEST_LENGTH 16\n#endif\n\n#ifndef NGX_HTTP_LUA_MAX_ARGS\n#   define NGX_HTTP_LUA_MAX_ARGS 100\n#endif\n\n#ifndef NGX_HTTP_LUA_MAX_HEADERS\n#   define NGX_HTTP_LUA_MAX_HEADERS 100\n#endif\n\n\n/* Nginx HTTP Lua Inline tag prefix */\n\n#define NGX_HTTP_LUA_INLINE_TAG \"nhli_\"\n\n#define NGX_HTTP_LUA_INLINE_TAG_LEN                                          \\\n    (sizeof(NGX_HTTP_LUA_INLINE_TAG) - 1)\n\n#define NGX_HTTP_LUA_INLINE_KEY_LEN                                          \\\n    (NGX_HTTP_LUA_INLINE_TAG_LEN + 2 * MD5_DIGEST_LENGTH)\n\n/* Nginx HTTP Lua File tag prefix */\n\n#define NGX_HTTP_LUA_FILE_TAG \"nhlf_\"\n\n#define NGX_HTTP_LUA_FILE_TAG_LEN                                            \\\n    (sizeof(NGX_HTTP_LUA_FILE_TAG) - 1)\n\n#define NGX_HTTP_LUA_FILE_KEY_LEN                                            \\\n    (NGX_HTTP_LUA_FILE_TAG_LEN + 2 * MD5_DIGEST_LENGTH)\n\n\n/* must be within 32 bits */\n#define NGX_HTTP_LUA_CONTEXT_SET                0x00000001\n#define NGX_HTTP_LUA_CONTEXT_REWRITE            0x00000002\n#define NGX_HTTP_LUA_CONTEXT_ACCESS             0x00000004\n#define NGX_HTTP_LUA_CONTEXT_CONTENT            0x00000008\n#define NGX_HTTP_LUA_CONTEXT_LOG                0x00000010\n#define NGX_HTTP_LUA_CONTEXT_HEADER_FILTER      0x00000020\n#define NGX_HTTP_LUA_CONTEXT_BODY_FILTER        0x00000040\n#define NGX_HTTP_LUA_CONTEXT_TIMER              0x00000080\n#define NGX_HTTP_LUA_CONTEXT_INIT_WORKER        0x00000100\n#define NGX_HTTP_LUA_CONTEXT_BALANCER           0x00000200\n#define NGX_HTTP_LUA_CONTEXT_SSL_CERT           0x00000400\n#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE     0x00000800\n#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH     0x00001000\n#define NGX_HTTP_LUA_CONTEXT_EXIT_WORKER        0x00002000\n#define NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO   0x00004000\n#define NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE     0x00008000\n#define NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY   0x00010000\n#define NGX_HTTP_LUA_CONTEXT_PRECONTENT         0x00020000\n#define NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT     0x00040000\n\n#define NGX_HTTP_LUA_FFI_NO_REQ_CTX         -100\n#define NGX_HTTP_LUA_FFI_BAD_CONTEXT        -101\n\n\n#if (NGX_PTR_SIZE >= 8 && !defined(_WIN64))\n#   define ngx_http_lua_lightudata_mask(ludata)                              \\\n        ((void *) ((uintptr_t) (&ngx_http_lua_##ludata) & ((1UL << 47) - 1)))\n#else\n#   define ngx_http_lua_lightudata_mask(ludata)                              \\\n        (&ngx_http_lua_##ludata)\n#endif\n\n\ntypedef struct ngx_http_lua_co_ctx_s  ngx_http_lua_co_ctx_t;\n\ntypedef struct ngx_http_lua_sema_mm_s  ngx_http_lua_sema_mm_t;\n\ntypedef struct ngx_http_lua_srv_conf_s  ngx_http_lua_srv_conf_t;\n\ntypedef struct ngx_http_lua_main_conf_s  ngx_http_lua_main_conf_t;\n\ntypedef struct ngx_http_lua_loc_conf_s  ngx_http_lua_loc_conf_t;\n\ntypedef struct ngx_http_lua_header_val_s  ngx_http_lua_header_val_t;\n\ntypedef struct ngx_http_lua_posted_thread_s  ngx_http_lua_posted_thread_t;\n\ntypedef struct ngx_http_lua_balancer_peer_data_s\n    ngx_http_lua_balancer_peer_data_t;\n\ntypedef ngx_int_t (*ngx_http_lua_main_conf_handler_pt)(ngx_log_t *log,\n    ngx_http_lua_main_conf_t *lmcf, lua_State *L);\n\ntypedef ngx_int_t (*ngx_http_lua_srv_conf_handler_pt)(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L);\n\ntypedef ngx_int_t (*ngx_http_lua_loc_conf_handler_pt)(ngx_http_request_t *r,\n    ngx_http_lua_loc_conf_t *llcf, lua_State *L);\n\ntypedef ngx_int_t (*ngx_http_lua_set_header_pt)(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\n\n\ntypedef struct {\n    u_char              *package;\n    lua_CFunction        loader;\n} ngx_http_lua_preload_hook_t;\n\n\ntypedef struct {\n    int             ref;\n    lua_State      *co;\n    ngx_queue_t     queue;\n} ngx_http_lua_thread_ref_t;\n\n\nstruct ngx_http_lua_main_conf_s {\n    lua_State           *lua;\n    ngx_pool_cleanup_t  *vm_cleanup;\n\n    ngx_str_t            lua_path;\n    ngx_str_t            lua_cpath;\n\n    ngx_cycle_t         *cycle;\n    ngx_pool_t          *pool;\n\n    ngx_int_t            max_pending_timers;\n    ngx_int_t            pending_timers;\n\n    ngx_int_t            max_running_timers;\n    ngx_int_t            running_timers;\n\n    ngx_connection_t    *watcher;  /* for watching the process exit event */\n\n    ngx_int_t            lua_thread_cache_max_entries;\n\n    ngx_hash_t           builtin_headers_out;\n\n#if (NGX_PCRE)\n    ngx_int_t            regex_cache_entries;\n    ngx_int_t            regex_cache_max_entries;\n    ngx_int_t            regex_match_limit;\n#endif\n\n#if (LUA_HAVE_PCRE_JIT)\n#if (NGX_PCRE2)\n    pcre2_jit_stack     *jit_stack;\n#else\n    pcre_jit_stack      *jit_stack;\n#endif\n#endif\n\n    ngx_array_t         *shm_zones;  /* of ngx_shm_zone_t* */\n\n    ngx_array_t         *shdict_zones; /* shm zones of \"shdict\" */\n\n    ngx_array_t         *preload_hooks; /* of ngx_http_lua_preload_hook_t */\n\n    ngx_flag_t           postponed_to_rewrite_phase_end;\n    ngx_flag_t           postponed_to_access_phase_end;\n    ngx_flag_t           postponed_to_precontent_phase_end;\n\n    ngx_http_lua_main_conf_handler_pt    init_handler;\n    ngx_str_t                            init_src;\n    u_char                              *init_chunkname;\n\n    ngx_http_lua_main_conf_handler_pt    init_worker_handler;\n    ngx_str_t                            init_worker_src;\n    u_char                              *init_worker_chunkname;\n\n    ngx_http_lua_main_conf_handler_pt    exit_worker_handler;\n    ngx_str_t                            exit_worker_src;\n    u_char                              *exit_worker_chunkname;\n\n    ngx_chain_t                            *body_filter_chain;\n                    /* neither yielding nor recursion is possible in\n                     * body_filter_by_lua*, so there cannot be any races among\n                     * concurrent requests when storing the chain\n                     * data pointer in the main conf.\n                     */\n\n    ngx_http_variable_value_t              *setby_args;\n                    /* neither yielding nor recursion is possible in\n                     * set_by_lua*, so there cannot be any races among\n                     * concurrent requests when storing the args pointer\n                     * in the main conf.\n                     */\n\n    size_t                                  setby_nargs;\n                    /* neither yielding nor recursion is possible in\n                     * set_by_lua*, so there cannot be any races among\n                     * concurrent requests when storing the nargs in the\n                     * main conf.\n                     */\n\n    ngx_uint_t                      shm_zones_inited;\n\n    ngx_http_lua_sema_mm_t         *sema_mm;\n\n    ngx_uint_t           malloc_trim_cycle;  /* a cycle is defined as the number\n                                                of requests */\n    ngx_uint_t           malloc_trim_req_count;\n\n    ngx_uint_t           directive_line;\n\n#if (nginx_version >= 1011011)\n    /* the following 2 fields are only used by ngx.req.raw_headers() for now */\n    ngx_buf_t          **busy_buf_ptrs;\n    ngx_int_t            busy_buf_ptr_count;\n#endif\n\n    ngx_int_t            host_var_index;\n\n    ngx_flag_t           set_sa_restart;\n\n    ngx_queue_t          free_lua_threads;  /* of ngx_http_lua_thread_ref_t */\n    ngx_queue_t          cached_lua_threads;  /* of ngx_http_lua_thread_ref_t */\n\n    ngx_uint_t           worker_thread_vm_pool_size;\n\n    unsigned             requires_header_filter:1;\n    unsigned             requires_body_filter:1;\n    unsigned             requires_capture_filter:1;\n    unsigned             requires_rewrite:1;\n    unsigned             requires_access:1;\n    unsigned             requires_log:1;\n    unsigned             requires_shm:1;\n    unsigned             requires_capture_log:1;\n    unsigned             requires_server_rewrite:1;\n    unsigned             requires_precontent:1;\n};\n\n\nstruct ngx_http_lua_srv_conf_s {\n    struct {\n#if (NGX_HTTP_SSL)\n        ngx_http_lua_srv_conf_handler_pt     ssl_cert_handler;\n        ngx_str_t                            ssl_cert_src;\n        u_char                              *ssl_cert_src_key;\n        u_char                              *ssl_cert_chunkname;\n        int                                  ssl_cert_src_ref;\n\n        ngx_http_lua_srv_conf_handler_pt     ssl_sess_store_handler;\n        ngx_str_t                            ssl_sess_store_src;\n        u_char                              *ssl_sess_store_src_key;\n        u_char                              *ssl_sess_store_chunkname;\n        int                                  ssl_sess_store_src_ref;\n\n        ngx_http_lua_srv_conf_handler_pt     ssl_sess_fetch_handler;\n        ngx_str_t                            ssl_sess_fetch_src;\n        u_char                              *ssl_sess_fetch_src_key;\n        u_char                              *ssl_sess_fetch_chunkname;\n        int                                  ssl_sess_fetch_src_ref;\n\n        ngx_http_lua_srv_conf_handler_pt     ssl_client_hello_handler;\n        ngx_str_t                            ssl_client_hello_src;\n        u_char                              *ssl_client_hello_src_key;\n        u_char                              *ssl_client_hello_chunkname;\n        int                                  ssl_client_hello_src_ref;\n#endif\n\n        ngx_http_lua_srv_conf_handler_pt     server_rewrite_handler;\n        ngx_http_complex_value_t             server_rewrite_src;\n        u_char                              *server_rewrite_src_key;\n        u_char                              *server_rewrite_chunkname;\n        int                                  server_rewrite_src_ref;\n    } srv;\n\n    struct {\n        ngx_uint_t                           max_cached;\n        ngx_queue_t                          cache;\n        ngx_queue_t                          free;\n        ngx_queue_t                         *buckets;\n        ngx_uint_t                           bucket_cnt;\n        ngx_http_upstream_init_pt            original_init_upstream;\n        ngx_http_upstream_init_peer_pt       original_init_peer;\n\n        ngx_http_lua_srv_conf_handler_pt     handler;\n        ngx_str_t                            src;\n        u_char                              *src_key;\n        u_char                              *chunkname;\n        int                                  src_ref;\n    } balancer;\n};\n\n\nstruct ngx_http_lua_loc_conf_s {\n#if (NGX_HTTP_SSL)\n    ngx_ssl_t              *ssl;  /* shared by SSL cosockets */\n    ngx_array_t            *ssl_certificates;\n    ngx_array_t            *ssl_certificate_keys;\n    ngx_uint_t              ssl_protocols;\n    ngx_str_t               ssl_ciphers;\n    ngx_uint_t              ssl_verify_depth;\n    ngx_str_t               ssl_trusted_certificate;\n    ngx_str_t               ssl_crl;\n    ngx_str_t               ssl_key_log;\n#if (nginx_version >= 1019004)\n    ngx_array_t            *ssl_conf_commands;\n#endif\n\n#if HAVE_LUA_PROXY_SSL\n    ngx_http_lua_loc_conf_handler_pt       proxy_ssl_cert_handler;\n    ngx_str_t                              proxy_ssl_cert_src;\n    u_char                                *proxy_ssl_cert_src_key;\n    u_char                                *proxy_ssl_cert_chunkname;\n    int                                    proxy_ssl_cert_src_ref;\n\n    ngx_http_lua_loc_conf_handler_pt       proxy_ssl_verify_handler;\n    ngx_str_t                              proxy_ssl_verify_src;\n    u_char                                *proxy_ssl_verify_src_key;\n    u_char                                *proxy_ssl_verify_chunkname;\n    int                                    proxy_ssl_verify_src_ref;\n    ngx_flag_t                             upstream_skip_openssl_default_verify;\n#endif\n\n#endif\n\n    ngx_flag_t              force_read_body; /* whether force request body to\n                                                be read */\n\n    ngx_flag_t              enable_code_cache; /* whether to enable\n                                                  code cache */\n\n    ngx_flag_t              http10_buffering;\n\n    ngx_http_handler_pt     rewrite_handler;\n    ngx_http_handler_pt     access_handler;\n    ngx_http_handler_pt     precontent_handler;\n    ngx_http_handler_pt     content_handler;\n    ngx_http_handler_pt     log_handler;\n    ngx_http_handler_pt     header_filter_handler;\n\n    ngx_http_output_body_filter_pt         body_filter_handler;\n\n\n\n    u_char                  *rewrite_chunkname;\n    ngx_http_complex_value_t rewrite_src;    /*  rewrite_by_lua\n                                                inline script/script\n                                                file path */\n\n    u_char                  *rewrite_src_key; /* cached key for rewrite_src */\n    int                      rewrite_src_ref;\n\n    u_char                  *access_chunkname;\n    ngx_http_complex_value_t access_src;     /*  access_by_lua\n                                                inline script/script\n                                                file path */\n\n    u_char                  *access_src_key; /* cached key for access_src */\n    int                      access_src_ref;\n\n    u_char                  *precontent_chunkname;\n    ngx_http_complex_value_t precontent_src;    /*  precontent_by_lua\n                                                inline script/script\n                                                file path */\n\n    u_char                  *precontent_src_key; /* cached key for\n                                                    precontent_src */\n    int                      precontent_src_ref;\n\n    u_char                  *content_chunkname;\n    ngx_http_complex_value_t content_src;    /*  content_by_lua\n                                                inline script/script\n                                                file path */\n\n    u_char                  *content_src_key; /* cached key for content_src */\n    int                      content_src_ref;\n\n\n    u_char                      *log_chunkname;\n    ngx_http_complex_value_t     log_src;     /* log_by_lua inline script/script\n                                                 file path */\n\n    u_char                      *log_src_key; /* cached key for log_src */\n    int                          log_src_ref;\n\n    ngx_http_complex_value_t header_filter_src;  /*  header_filter_by_lua\n                                                     inline script/script\n                                                     file path */\n\n    u_char                 *header_filter_chunkname;\n    u_char                 *header_filter_src_key;\n                                    /* cached key for header_filter_src */\n    int                     header_filter_src_ref;\n\n\n    ngx_http_complex_value_t         body_filter_src;\n    u_char                          *body_filter_src_key;\n    u_char                          *body_filter_chunkname;\n    int                              body_filter_src_ref;\n\n    ngx_msec_t                       keepalive_timeout;\n    ngx_msec_t                       connect_timeout;\n    ngx_msec_t                       send_timeout;\n    ngx_msec_t                       read_timeout;\n\n    size_t                           send_lowat;\n    size_t                           buffer_size;\n\n    ngx_uint_t                       pool_size;\n\n    ngx_flag_t                       transform_underscores_in_resp_headers;\n    ngx_flag_t                       log_socket_errors;\n    ngx_flag_t                       check_client_abort;\n    ngx_flag_t                       use_default_type;\n};\n\n\ntypedef enum {\n    NGX_HTTP_LUA_USER_CORO_NOP      = 0,\n    NGX_HTTP_LUA_USER_CORO_RESUME   = 1,\n    NGX_HTTP_LUA_USER_CORO_YIELD    = 2,\n    NGX_HTTP_LUA_USER_THREAD_RESUME = 3,\n} ngx_http_lua_user_coro_op_t;\n\n\ntypedef enum {\n    NGX_HTTP_LUA_CO_RUNNING   = 0, /* coroutine running */\n    NGX_HTTP_LUA_CO_SUSPENDED = 1, /* coroutine suspended */\n    NGX_HTTP_LUA_CO_NORMAL    = 2, /* coroutine normal */\n    NGX_HTTP_LUA_CO_DEAD      = 3, /* coroutine dead */\n    NGX_HTTP_LUA_CO_ZOMBIE    = 4, /* coroutine zombie */\n} ngx_http_lua_co_status_t;\n\n\nstruct ngx_http_lua_posted_thread_s {\n    ngx_http_lua_co_ctx_t               *co_ctx;\n    ngx_http_lua_posted_thread_t        *next;\n};\n\n\nstruct ngx_http_lua_co_ctx_s {\n    void                    *data;      /* user state for cosockets */\n\n    lua_State               *co;\n    ngx_http_lua_co_ctx_t   *parent_co_ctx;\n\n    ngx_http_lua_posted_thread_t    *zombie_child_threads;\n    ngx_http_lua_posted_thread_t   **next_zombie_child_thread;\n\n    ngx_http_cleanup_pt      cleanup;\n\n    ngx_int_t               *sr_statuses; /* all capture subrequest statuses */\n\n    ngx_http_headers_out_t **sr_headers;\n\n    ngx_str_t               *sr_bodies;   /* all captured subrequest bodies */\n\n    uint8_t                 *sr_flags;\n\n    unsigned                 nresults_from_worker_thread;  /* number of results\n                                                            * from worker\n                                                            * thread callback */\n    unsigned                 nrets;     /* ngx_http_lua_run_thread nrets arg. */\n\n    unsigned                 nsubreqs;  /* number of subrequests of the\n                                         * current request */\n\n    unsigned                 pending_subreqs; /* number of subrequests being\n                                                 waited */\n\n    ngx_event_t              sleep;  /* used for ngx.sleep */\n\n    ngx_queue_t              sem_wait_queue;\n\n#ifdef NGX_LUA_USE_ASSERT\n    int                      co_top; /* stack top after yielding/creation,\n                                        only for sanity checks */\n#endif\n\n    int                      co_ref; /*  reference to anchor the thread\n                                         coroutines (entry coroutine and user\n                                         threads) in the Lua registry,\n                                         preventing the thread coroutine\n                                         from beging collected by the\n                                         Lua GC */\n\n    unsigned                 waited_by_parent:1;  /* whether being waited by\n                                                     a parent coroutine */\n\n    unsigned                 co_status:3;  /* the current coroutine's status */\n\n    unsigned                 flushing:1; /* indicates whether the current\n                                            coroutine is waiting for\n                                            ngx.flush(true) */\n\n    unsigned                 is_uthread:1; /* whether the current coroutine is\n                                              a user thread */\n\n    unsigned                 thread_spawn_yielded:1; /* yielded from\n                                                        the ngx.thread.spawn()\n                                                        call */\n    unsigned                 sem_resume_status:1;\n\n    unsigned                 is_wrap:1; /* set when creating coroutines via\n                                           coroutine.wrap */\n\n    unsigned                 propagate_error:1; /* set when propagating an error\n                                                   from a coroutine to its\n                                                   parent */\n};\n\n\ntypedef struct {\n    lua_State       *vm;\n    ngx_int_t        count;\n} ngx_http_lua_vm_state_t;\n\n\ntypedef struct ngx_http_lua_ctx_s {\n    /* for lua_code_cache off: */\n    ngx_http_lua_vm_state_t  *vm_state;\n\n    ngx_http_request_t      *request;\n    ngx_http_handler_pt      resume_handler;\n\n    ngx_http_lua_co_ctx_t   *cur_co_ctx; /* co ctx for the current coroutine */\n\n    /* FIXME: we should use rbtree here to prevent O(n) lookup overhead */\n    ngx_list_t              *user_co_ctx; /* coroutine contexts for user\n                                             coroutines */\n\n    ngx_http_lua_co_ctx_t    entry_co_ctx; /* coroutine context for the\n                                              entry coroutine */\n\n    ngx_http_lua_co_ctx_t   *on_abort_co_ctx; /* coroutine context for the\n                                                 on_abort thread */\n\n    int                      ctx_ref;  /*  reference to anchor\n                                           request ctx data in lua\n                                           registry */\n\n    unsigned                 flushing_coros; /* number of coroutines waiting on\n                                                ngx.flush(true) */\n\n    ngx_chain_t             *out;  /* buffered output chain for HTTP 1.0 */\n    ngx_chain_t             *free_bufs;\n    ngx_chain_t             *busy_bufs;\n    ngx_chain_t             *free_recv_bufs;\n\n    ngx_chain_t             *filter_in_bufs;  /* for the body filter */\n    ngx_chain_t             *filter_busy_bufs;  /* for the body filter */\n\n    ngx_pool_cleanup_pt     *cleanup;\n\n    ngx_http_cleanup_t      *free_cleanup; /* free list of cleanup records */\n\n    ngx_chain_t             *body; /* buffered subrequest response body\n                                      chains */\n\n    ngx_chain_t            **last_body; /* for the \"body\" field */\n\n    ngx_str_t                exec_uri;\n    ngx_str_t                exec_args;\n\n    ngx_int_t                exit_code;\n\n    void                    *downstream;  /* can be either\n                                             ngx_http_lua_socket_tcp_upstream_t\n                                             or ngx_http_lua_co_ctx_t */\n\n    ngx_uint_t               index;              /* index of the current\n                                                    subrequest in its parent\n                                                    request */\n\n    ngx_http_lua_posted_thread_t   *posted_threads;\n\n    int                      uthreads; /* number of active user threads */\n\n    uint32_t                 context;   /* the current running directive context\n                                           (or running phase) for the current\n                                           Lua chunk */\n\n    unsigned                 run_post_subrequest:1; /* whether it has run\n                                                       post_subrequest\n                                                       (for subrequests only) */\n\n    unsigned                 waiting_more_body:1;   /* 1: waiting for more\n                                                       request body data;\n                                                       0: no need to wait */\n\n    unsigned         co_op:2; /*  coroutine API operation */\n\n    unsigned         exited:1;\n\n    unsigned         eof:1;             /*  1: last_buf has been sent;\n                                            0: last_buf not sent yet */\n\n    unsigned         capture:1;  /*  1: response body of current request\n                                        is to be captured by the lua\n                                        capture filter,\n                                     0: not to be captured */\n\n\n    unsigned         read_body_done:1;      /* 1: request body has been all\n                                               read; 0: body has not been\n                                               all read */\n\n    unsigned         headers_set:1; /* whether the user has set custom\n                                       response headers */\n    unsigned         mime_set:1;    /* whether the user has set Content-Type\n                                       response header */\n    unsigned         entered_server_rewrite_phase:1;\n    unsigned         entered_rewrite_phase:1;\n    unsigned         entered_access_phase:1;\n    unsigned         entered_precontent_phase:1;\n    unsigned         entered_content_phase:1;\n\n    unsigned         buffering:1; /* HTTP 1.0 response body buffering flag */\n\n    unsigned         no_abort:1; /* prohibit \"world abortion\" via ngx.exit()\n                                    and etc */\n\n    unsigned         header_sent:1; /* r->header_sent is not sufficient for\n                                     * this because special header filters\n                                     * like ngx_image_filter may intercept\n                                     * the header. so we should always test\n                                     * both flags. see the test case in\n                                     * t/020-subrequest.t */\n\n    unsigned         seen_last_in_filter:1;  /* used by body_filter_by_lua* */\n    unsigned         seen_last_for_subreq:1; /* used by body capture filter */\n    unsigned         writing_raw_req_socket:1; /* used by raw downstream\n                                                  socket */\n    unsigned         acquired_raw_req_socket:1;  /* whether a raw req socket\n                                                    is acquired */\n    unsigned         seen_body_data:1;\n} ngx_http_lua_ctx_t;\n\n\nstruct ngx_http_lua_header_val_s {\n    ngx_http_complex_value_t                value;\n    ngx_uint_t                              hash;\n    ngx_str_t                               key;\n    ngx_http_lua_set_header_pt              handler;\n    ngx_uint_t                              offset;\n    unsigned                                no_override;\n};\n\n\ntypedef struct {\n    ngx_str_t                               name;\n    ngx_uint_t                              offset;\n    ngx_http_lua_set_header_pt              handler;\n} ngx_http_lua_set_header_t;\n\n\nextern ngx_module_t ngx_http_lua_module;\nextern ngx_http_output_header_filter_pt ngx_http_lua_next_header_filter;\nextern ngx_http_output_body_filter_pt ngx_http_lua_next_body_filter;\n\n\n#endif /* _NGX_HTTP_LUA_COMMON_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_config.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_config.h\"\n#include \"api/ngx_http_lua_api.h\"\n\n\nstatic int ngx_http_lua_config_prefix(lua_State *L);\nstatic int ngx_http_lua_config_configure(lua_State *L);\n\n\nvoid\nngx_http_lua_inject_config_api(lua_State *L)\n{\n    /* ngx.config */\n\n    lua_createtable(L, 0, 6 /* nrec */);    /* .config */\n\n#if (NGX_DEBUG)\n    lua_pushboolean(L, 1);\n#else\n    lua_pushboolean(L, 0);\n#endif\n    lua_setfield(L, -2, \"debug\");\n\n    lua_pushcfunction(L, ngx_http_lua_config_prefix);\n    lua_setfield(L, -2, \"prefix\");\n\n    lua_pushinteger(L, nginx_version);\n    lua_setfield(L, -2, \"nginx_version\");\n\n    lua_pushinteger(L, ngx_http_lua_version);\n    lua_setfield(L, -2, \"ngx_lua_version\");\n\n    lua_pushcfunction(L, ngx_http_lua_config_configure);\n    lua_setfield(L, -2, \"nginx_configure\");\n\n    lua_pushliteral(L, \"http\");\n    lua_setfield(L, -2, \"subsystem\");\n\n    lua_setfield(L, -2, \"config\");\n}\n\n\nstatic int\nngx_http_lua_config_prefix(lua_State *L)\n{\n    lua_pushlstring(L, (char *) ngx_cycle->prefix.data,\n                    ngx_cycle->prefix.len);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_config_configure(lua_State *L)\n{\n    lua_pushliteral(L, NGX_CONFIGURE);\n    return 1;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_config.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_CONFIG_H_INCLUDED_\n#define _NGX_HTTP_LUA_CONFIG_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_config_api(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_CONFIG_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_consts.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_consts.h\"\n\n\nvoid\nngx_http_lua_inject_core_consts(lua_State *L)\n{\n    /* {{{ core constants */\n    lua_pushinteger(L, NGX_OK);\n    lua_setfield(L, -2, \"OK\");\n\n    lua_pushinteger(L, NGX_AGAIN);\n    lua_setfield(L, -2, \"AGAIN\");\n\n    lua_pushinteger(L, NGX_DONE);\n    lua_setfield(L, -2, \"DONE\");\n\n    lua_pushinteger(L, NGX_DECLINED);\n    lua_setfield(L, -2, \"DECLINED\");\n\n    lua_pushinteger(L, NGX_ERROR);\n    lua_setfield(L, -2, \"ERROR\");\n\n    lua_pushlightuserdata(L, NULL);\n    lua_setfield(L, -2, \"null\");\n    /* }}} */\n}\n\n\nvoid\nngx_http_lua_inject_http_consts(lua_State *L)\n{\n    /* {{{ HTTP status constants */\n    lua_pushinteger(L, NGX_HTTP_GET);\n    lua_setfield(L, -2, \"HTTP_GET\");\n\n    lua_pushinteger(L, NGX_HTTP_POST);\n    lua_setfield(L, -2, \"HTTP_POST\");\n\n    lua_pushinteger(L, NGX_HTTP_PUT);\n    lua_setfield(L, -2, \"HTTP_PUT\");\n\n    lua_pushinteger(L, NGX_HTTP_HEAD);\n    lua_setfield(L, -2, \"HTTP_HEAD\");\n\n    lua_pushinteger(L, NGX_HTTP_DELETE);\n    lua_setfield(L, -2, \"HTTP_DELETE\");\n\n    lua_pushinteger(L, NGX_HTTP_OPTIONS);\n    lua_setfield(L, -2, \"HTTP_OPTIONS\");\n\n    lua_pushinteger(L, NGX_HTTP_MKCOL);\n    lua_setfield(L, -2, \"HTTP_MKCOL\");\n\n    lua_pushinteger(L, NGX_HTTP_COPY);\n    lua_setfield(L, -2, \"HTTP_COPY\");\n\n    lua_pushinteger(L, NGX_HTTP_MOVE);\n    lua_setfield(L, -2, \"HTTP_MOVE\");\n\n    lua_pushinteger(L, NGX_HTTP_PROPFIND);\n    lua_setfield(L, -2, \"HTTP_PROPFIND\");\n\n    lua_pushinteger(L, NGX_HTTP_PROPPATCH);\n    lua_setfield(L, -2, \"HTTP_PROPPATCH\");\n\n    lua_pushinteger(L, NGX_HTTP_LOCK);\n    lua_setfield(L, -2, \"HTTP_LOCK\");\n\n    lua_pushinteger(L, NGX_HTTP_UNLOCK);\n    lua_setfield(L, -2, \"HTTP_UNLOCK\");\n\n    lua_pushinteger(L, NGX_HTTP_PATCH);\n    lua_setfield(L, -2, \"HTTP_PATCH\");\n\n    lua_pushinteger(L, NGX_HTTP_TRACE);\n    lua_setfield(L, -2, \"HTTP_TRACE\");\n    /* }}} */\n\n    lua_pushinteger(L, NGX_HTTP_CONTINUE);\n    lua_setfield(L, -2, \"HTTP_CONTINUE\");\n\n    lua_pushinteger(L, NGX_HTTP_SWITCHING_PROTOCOLS);\n    lua_setfield(L, -2, \"HTTP_SWITCHING_PROTOCOLS\");\n\n    lua_pushinteger(L, NGX_HTTP_OK);\n    lua_setfield(L, -2, \"HTTP_OK\");\n\n    lua_pushinteger(L, NGX_HTTP_CREATED);\n    lua_setfield(L, -2, \"HTTP_CREATED\");\n\n    lua_pushinteger(L, NGX_HTTP_ACCEPTED);\n    lua_setfield(L, -2, \"HTTP_ACCEPTED\");\n\n    lua_pushinteger(L, NGX_HTTP_NO_CONTENT);\n    lua_setfield(L, -2, \"HTTP_NO_CONTENT\");\n\n    lua_pushinteger(L, NGX_HTTP_PARTIAL_CONTENT);\n    lua_setfield(L, -2, \"HTTP_PARTIAL_CONTENT\");\n\n    lua_pushinteger(L, NGX_HTTP_SPECIAL_RESPONSE);\n    lua_setfield(L, -2, \"HTTP_SPECIAL_RESPONSE\");\n\n    lua_pushinteger(L, NGX_HTTP_MOVED_PERMANENTLY);\n    lua_setfield(L, -2, \"HTTP_MOVED_PERMANENTLY\");\n\n    lua_pushinteger(L, NGX_HTTP_MOVED_TEMPORARILY);\n    lua_setfield(L, -2, \"HTTP_MOVED_TEMPORARILY\");\n\n    lua_pushinteger(L, NGX_HTTP_SEE_OTHER);\n    lua_setfield(L, -2, \"HTTP_SEE_OTHER\");\n\n    lua_pushinteger(L, NGX_HTTP_PERMANENT_REDIRECT);\n    lua_setfield(L, -2, \"HTTP_PERMANENT_REDIRECT\");\n\n    lua_pushinteger(L, NGX_HTTP_NOT_MODIFIED);\n    lua_setfield(L, -2, \"HTTP_NOT_MODIFIED\");\n\n    lua_pushinteger(L, NGX_HTTP_TEMPORARY_REDIRECT);\n    lua_setfield(L, -2, \"HTTP_TEMPORARY_REDIRECT\");\n\n    lua_pushinteger(L, NGX_HTTP_BAD_REQUEST);\n    lua_setfield(L, -2, \"HTTP_BAD_REQUEST\");\n\n    lua_pushinteger(L, NGX_HTTP_UNAUTHORIZED);\n    lua_setfield(L, -2, \"HTTP_UNAUTHORIZED\");\n\n    lua_pushinteger(L, 402);\n    lua_setfield(L, -2, \"HTTP_PAYMENT_REQUIRED\");\n\n    lua_pushinteger(L, NGX_HTTP_FORBIDDEN);\n    lua_setfield(L, -2, \"HTTP_FORBIDDEN\");\n\n    lua_pushinteger(L, NGX_HTTP_NOT_FOUND);\n    lua_setfield(L, -2, \"HTTP_NOT_FOUND\");\n\n    lua_pushinteger(L, NGX_HTTP_NOT_ALLOWED);\n    lua_setfield(L, -2, \"HTTP_NOT_ALLOWED\");\n\n    lua_pushinteger(L, 406);\n    lua_setfield(L, -2, \"HTTP_NOT_ACCEPTABLE\");\n\n    lua_pushinteger(L, NGX_HTTP_REQUEST_TIME_OUT);\n    lua_setfield(L, -2, \"HTTP_REQUEST_TIMEOUT\");\n\n    lua_pushinteger(L, NGX_HTTP_CONFLICT);\n    lua_setfield(L, -2, \"HTTP_CONFLICT\");\n\n    lua_pushinteger(L, 410);\n    lua_setfield(L, -2, \"HTTP_GONE\");\n\n    lua_pushinteger(L, 426);\n    lua_setfield(L, -2, \"HTTP_UPGRADE_REQUIRED\");\n\n    lua_pushinteger(L, 429);\n    lua_setfield(L, -2, \"HTTP_TOO_MANY_REQUESTS\");\n\n    lua_pushinteger(L, 451);\n    lua_setfield(L, -2, \"HTTP_ILLEGAL\");\n\n    lua_pushinteger(L, NGX_HTTP_CLOSE);\n    lua_setfield(L, -2, \"HTTP_CLOSE\");\n\n    lua_pushinteger(L, NGX_HTTP_INTERNAL_SERVER_ERROR);\n    lua_setfield(L, -2, \"HTTP_INTERNAL_SERVER_ERROR\");\n\n    lua_pushinteger(L, NGX_HTTP_NOT_IMPLEMENTED);\n    lua_setfield(L, -2, \"HTTP_NOT_IMPLEMENTED\");\n\n    /* keep for backward compatibility */\n    lua_pushinteger(L, NGX_HTTP_NOT_IMPLEMENTED);\n    lua_setfield(L, -2, \"HTTP_METHOD_NOT_IMPLEMENTED\");\n\n    lua_pushinteger(L, NGX_HTTP_BAD_GATEWAY);\n    lua_setfield(L, -2, \"HTTP_BAD_GATEWAY\");\n\n    lua_pushinteger(L, NGX_HTTP_SERVICE_UNAVAILABLE);\n    lua_setfield(L, -2, \"HTTP_SERVICE_UNAVAILABLE\");\n\n    lua_pushinteger(L, NGX_HTTP_GATEWAY_TIME_OUT);\n    lua_setfield(L, -2, \"HTTP_GATEWAY_TIMEOUT\");\n\n    lua_pushinteger(L, 505);\n    lua_setfield(L, -2, \"HTTP_VERSION_NOT_SUPPORTED\");\n\n    lua_pushinteger(L, NGX_HTTP_INSUFFICIENT_STORAGE);\n    lua_setfield(L, -2, \"HTTP_INSUFFICIENT_STORAGE\");\n\n    /* }}} */\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_consts.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_CONSTS_H_INCLUDED_\n#define _NGX_HTTP_LUA_CONSTS_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_http_consts(lua_State *L);\nvoid ngx_http_lua_inject_core_consts(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_CONSTS_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_contentby.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_exception.h\"\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_probe.h\"\n\n\nstatic void ngx_http_lua_content_phase_post_read(ngx_http_request_t *r);\n\n\nngx_int_t\nngx_http_lua_content_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    int                      co_ref;\n    ngx_int_t                rc;\n    lua_State               *co;\n    ngx_event_t             *rev;\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_pool_cleanup_t      *cln;\n\n    ngx_http_lua_loc_conf_t      *llcf;\n\n    dd(\"content by chunk\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n    } else {\n        dd(\"reset ctx\");\n        ngx_http_lua_reset_ctx(r, L, ctx);\n    }\n\n    ctx->entered_content_phase = 1;\n\n    /*  {{{ new coroutine to handle request */\n    co = ngx_http_lua_new_thread(r, L, &co_ref);\n\n    if (co == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"lua: failed to create new coroutine to handle request\");\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    /*  move code closure to new coroutine */\n    lua_xmove(L, co, 1);\n\n#ifndef OPENRESTY_LUAJIT\n    /*  set closure's env table to new coroutine's globals table */\n    ngx_http_lua_get_globals_table(co);\n    lua_setfenv(co, -2);\n#endif\n\n    /*  save nginx request in coroutine globals table */\n    ngx_http_lua_set_req(co, r);\n\n    ctx->cur_co_ctx = &ctx->entry_co_ctx;\n    ctx->cur_co_ctx->co = co;\n    ctx->cur_co_ctx->co_ref = co_ref;\n#ifdef NGX_LUA_USE_ASSERT\n    ctx->cur_co_ctx->co_top = 1;\n#endif\n\n    ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);\n\n    /*  {{{ register request cleanup hooks */\n    if (ctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n        if (cln == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        cln->handler = ngx_http_lua_request_cleanup_handler;\n        cln->data = ctx;\n        ctx->cleanup = &cln->handler;\n    }\n    /*  }}} */\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_CONTENT;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->check_client_abort) {\n        r->read_event_handler = ngx_http_lua_rd_check_broken_connection;\n\n#if (NGX_HTTP_V2)\n        if (!r->stream) {\n#endif\n\n        rev = r->connection->read;\n\n        if (!rev->active) {\n            if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n\n#if (NGX_HTTP_V2)\n        }\n#endif\n\n    } else {\n        r->read_event_handler = ngx_http_block_reading;\n    }\n\n    rc = ngx_http_lua_run_thread(L, r, ctx, 0);\n\n    if (rc == NGX_ERROR || rc >= NGX_OK) {\n        return rc;\n    }\n\n    if (rc == NGX_AGAIN) {\n        return ngx_http_lua_content_run_posted_threads(L, r, ctx, 0);\n    }\n\n    if (rc == NGX_DONE) {\n        return ngx_http_lua_content_run_posted_threads(L, r, ctx, 1);\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_lua_content_wev_handler(ngx_http_request_t *r)\n{\n    ngx_http_lua_ctx_t          *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return;\n    }\n\n    (void) ctx->resume_handler(r);\n}\n\n\nngx_int_t\nngx_http_lua_content_handler(ngx_http_request_t *r)\n{\n    ngx_http_lua_loc_conf_t     *llcf;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_int_t                    rc;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua content handler, uri:\\\"%V\\\" c:%ud\", &r->uri,\n                   r->main->count);\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->content_handler == NULL) {\n        dd(\"no content handler found\");\n        return NGX_DECLINED;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    dd(\"ctx = %p\", ctx);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    dd(\"entered? %d\", (int) ctx->entered_content_phase);\n\n    if (ctx->waiting_more_body) {\n        return NGX_DONE;\n    }\n\n    if (ctx->entered_content_phase) {\n        dd(\"calling wev handler\");\n        rc = ctx->resume_handler(r);\n        dd(\"wev handler returns %d\", (int) rc);\n        return rc;\n    }\n\n    if (llcf->force_read_body && !ctx->read_body_done) {\n        r->request_body_in_single_buf = 1;\n        r->request_body_in_persistent_file = 1;\n        r->request_body_in_clean_file = 1;\n\n        rc = ngx_http_read_client_request_body(r,\n                                        ngx_http_lua_content_phase_post_read);\n\n        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n\n        if (rc == NGX_AGAIN) {\n            ctx->waiting_more_body = 1;\n\n            return NGX_DONE;\n        }\n    }\n\n    dd(\"setting entered\");\n\n    ctx->entered_content_phase = 1;\n\n    dd(\"calling content handler\");\n    return llcf->content_handler(r);\n}\n\n\n/* post read callback for the content phase */\nstatic void\nngx_http_lua_content_phase_post_read(ngx_http_request_t *r)\n{\n    ngx_http_lua_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    ctx->read_body_done = 1;\n\n    if (ctx->waiting_more_body) {\n        ctx->waiting_more_body = 0;\n        ngx_http_lua_finalize_request(r, ngx_http_lua_content_handler(r));\n\n    } else {\n        r->main->count--;\n    }\n}\n\n\nngx_int_t\nngx_http_lua_content_handler_file(ngx_http_request_t *r)\n{\n    lua_State                       *L;\n    ngx_int_t                        rc;\n    u_char                          *script_path;\n    ngx_http_lua_loc_conf_t         *llcf;\n    ngx_str_t                        eval_src;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (ngx_http_complex_value(r, &llcf->content_src, &eval_src) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,\n                                           eval_src.len);\n\n    if (script_path == NULL) {\n        return NGX_ERROR;\n    }\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->content_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua script file (w/ cache)        sp = 1 */\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,\n                                     &llcf->content_src_ref,\n                                     llcf->content_src_key);\n    if (rc != NGX_OK) {\n        if (rc < NGX_HTTP_SPECIAL_RESPONSE) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_content_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_content_handler_inline(ngx_http_request_t *r)\n{\n    lua_State                   *L;\n    ngx_int_t                    rc;\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->content_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua inline script (w/ cache) sp = 1 */\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       llcf->content_src.value.data,\n                                       llcf->content_src.value.len,\n                                       &llcf->content_src_ref,\n                                       llcf->content_src_key,\n                                       (const char *)\n                                       llcf->content_chunkname);\n    if (rc != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    return ngx_http_lua_content_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_content_run_posted_threads(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, int n)\n{\n    ngx_int_t                        rc;\n    ngx_http_lua_posted_thread_t    *pt;\n\n    dd(\"run posted threads: %p\", ctx->posted_threads);\n\n    for ( ;; ) {\n        pt = ctx->posted_threads;\n        if (pt == NULL) {\n            goto done;\n        }\n\n        ctx->posted_threads = pt->next;\n\n        ngx_http_lua_probe_run_posted_thread(r, pt->co_ctx->co,\n                                             (int) pt->co_ctx->co_status);\n\n        dd(\"posted thread status: %d\", pt->co_ctx->co_status);\n\n        if (pt->co_ctx->co_status != NGX_HTTP_LUA_CO_RUNNING) {\n            continue;\n        }\n\n        ctx->cur_co_ctx = pt->co_ctx;\n\n        rc = ngx_http_lua_run_thread(L, r, ctx, 0);\n\n        if (rc == NGX_AGAIN) {\n            continue;\n        }\n\n        if (rc == NGX_DONE) {\n            n++;\n            continue;\n        }\n\n        if (rc == NGX_OK) {\n            while (n > 0) {\n                ngx_http_lua_finalize_request(r, NGX_DONE);\n                n--;\n            }\n\n            return NGX_OK;\n        }\n\n        /* rc == NGX_ERROR || rc > NGX_OK */\n\n        return rc;\n    }\n\ndone:\n\n    if (n == 1) {\n        return NGX_DONE;\n    }\n\n    if (n == 0) {\n        r->main->count++;\n        return NGX_DONE;\n    }\n\n    /* n > 1 */\n\n    do {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n    } while (--n > 1);\n\n    return NGX_DONE;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_contentby.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_CONTENT_BY_H_INCLUDED_\n#define _NGX_HTTP_LUA_CONTENT_BY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_content_by_chunk(lua_State *L, ngx_http_request_t *r);\nvoid ngx_http_lua_content_wev_handler(ngx_http_request_t *r);\nngx_int_t ngx_http_lua_content_handler_file(ngx_http_request_t *r);\nngx_int_t ngx_http_lua_content_handler_inline(ngx_http_request_t *r);\nngx_int_t ngx_http_lua_content_handler(ngx_http_request_t *r);\nngx_int_t ngx_http_lua_content_run_posted_threads(lua_State *L,\n    ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int n);\n\n\n#endif /* _NGX_HTTP_LUA_CONTENT_BY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_control.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_control.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_coroutine.h\"\n\n\nstatic int ngx_http_lua_ngx_exec(lua_State *L);\nstatic int ngx_http_lua_ngx_redirect(lua_State *L);\nstatic int ngx_http_lua_on_abort(lua_State *L);\n\n\nvoid\nngx_http_lua_inject_control_api(ngx_log_t *log, lua_State *L)\n{\n    /* ngx.redirect */\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_redirect);\n    lua_setfield(L, -2, \"redirect\");\n\n    /* ngx.exec */\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_exec);\n    lua_setfield(L, -2, \"exec\");\n\n    /* ngx.on_abort */\n\n    lua_pushcfunction(L, ngx_http_lua_on_abort);\n    lua_setfield(L, -2, \"on_abort\");\n}\n\n\nstatic int\nngx_http_lua_ngx_exec(lua_State *L)\n{\n    int                          n;\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_str_t                    uri;\n    ngx_str_t                    args, user_args;\n    ngx_uint_t                   flags;\n    u_char                      *p;\n    u_char                      *q;\n    size_t                       len;\n    const char                  *msg;\n\n    n = lua_gettop(L);\n    if (n != 1 && n != 2) {\n        return luaL_error(L, \"expecting one or two arguments, but got %d\",\n                          n);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request object found\");\n    }\n\n    ngx_str_null(&args);\n\n    /* read the 1st argument (uri) */\n\n    p = (u_char *) luaL_checklstring(L, 1, &len);\n\n    if (len == 0) {\n        return luaL_error(L, \"The uri argument is empty\");\n    }\n\n    uri.data = ngx_palloc(r->pool, len);\n    if (uri.data == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    ngx_memcpy(uri.data, p, len);\n\n    uri.len = len;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_ACCESS\n                               | NGX_HTTP_LUA_CONTEXT_PRECONTENT\n                               | NGX_HTTP_LUA_CONTEXT_CONTENT);\n\n    ngx_http_lua_check_if_abortable(L, ctx);\n\n    flags = NGX_HTTP_LOG_UNSAFE;\n\n    if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {\n        return luaL_error(L, \"unsafe uri\");\n    }\n\n    if (n == 2) {\n        /* read the 2nd argument (args) */\n        dd(\"args type: %s\", luaL_typename(L, 2));\n\n        switch (lua_type(L, 2)) {\n        case LUA_TNUMBER:\n        case LUA_TSTRING:\n            p = (u_char *) lua_tolstring(L, 2, &len);\n\n            user_args.data = ngx_palloc(r->pool, len);\n            if (user_args.data == NULL) {\n                return luaL_error(L, \"no memory\");\n            }\n\n            ngx_memcpy(user_args.data, p, len);\n\n            user_args.len = len;\n            break;\n\n        case LUA_TTABLE:\n            ngx_http_lua_process_args_option(r, L, 2, &user_args);\n\n            dd(\"user_args: %.*s\", (int) user_args.len, user_args.data);\n\n            break;\n\n        case LUA_TNIL:\n            ngx_str_null(&user_args);\n            break;\n\n        default:\n            msg = lua_pushfstring(L, \"string, number, or table expected, \"\n                                  \"but got %s\", luaL_typename(L, 2));\n            return luaL_argerror(L, 2, msg);\n        }\n\n    } else {\n        user_args.data = NULL;\n        user_args.len = 0;\n    }\n\n    if (user_args.len) {\n        if (args.len == 0) {\n            args = user_args;\n\n        } else {\n            p = ngx_palloc(r->pool, args.len + user_args.len + 1);\n            if (p == NULL) {\n                return luaL_error(L, \"no memory\");\n            }\n\n            q = ngx_copy(p, args.data, args.len);\n            *q++ = '&';\n            ngx_memcpy(q, user_args.data, user_args.len);\n\n            args.data = p;\n            args.len += user_args.len + 1;\n        }\n    }\n\n    if (r->header_sent || ctx->header_sent) {\n        return luaL_error(L, \"attempt to call ngx.exec after \"\n                          \"sending out response headers\");\n    }\n\n    ctx->exec_uri = uri;\n    ctx->exec_args = args;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua exec \\\"%V?%V\\\"\",\n                   &ctx->exec_uri, &ctx->exec_args);\n\n    return lua_yield(L, 0);\n}\n\n\nstatic int\nngx_http_lua_ngx_redirect(lua_State *L)\n{\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_int_t                    rc;\n    int                          n;\n    u_char                      *p;\n    u_char                      *uri;\n    u_char                       byte;\n    size_t                       len;\n    ngx_table_elt_t             *h;\n    ngx_http_request_t          *r;\n    size_t                       buf_len;\n    u_char                      *buf;\n\n    n = lua_gettop(L);\n\n    if (n != 1 && n != 2) {\n        return luaL_error(L, \"expecting one or two arguments\");\n    }\n\n    p = (u_char *) luaL_checklstring(L, 1, &len);\n\n    if (n == 2) {\n        rc = (ngx_int_t) luaL_checknumber(L, 2);\n\n        if (rc != NGX_HTTP_MOVED_TEMPORARILY\n            && rc != NGX_HTTP_MOVED_PERMANENTLY\n            && rc != NGX_HTTP_SEE_OTHER\n            && rc != NGX_HTTP_PERMANENT_REDIRECT\n            && rc != NGX_HTTP_TEMPORARY_REDIRECT)\n        {\n            return luaL_error(L, \"only ngx.HTTP_MOVED_TEMPORARILY, \"\n                              \"ngx.HTTP_MOVED_PERMANENTLY, \"\n                              \"ngx.HTTP_PERMANENT_REDIRECT, \"\n                              \"ngx.HTTP_SEE_OTHER, and \"\n                              \"ngx.HTTP_TEMPORARY_REDIRECT are allowed\");\n        }\n\n    } else {\n        rc = NGX_HTTP_MOVED_TEMPORARILY;\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request object found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_ACCESS\n                               | NGX_HTTP_LUA_CONTEXT_PRECONTENT\n                               | NGX_HTTP_LUA_CONTEXT_CONTENT);\n\n    ngx_http_lua_check_if_abortable(L, ctx);\n\n    if (r->header_sent || ctx->header_sent) {\n        return luaL_error(L, \"attempt to call ngx.redirect after sending out \"\n                          \"the headers\");\n    }\n\n    if (ngx_http_lua_check_unsafe_uri_bytes(r, p, len, &byte) != NGX_OK) {\n        buf_len = ngx_http_lua_escape_log(NULL, p, len) + 1;\n        buf = ngx_palloc(r->pool, buf_len);\n        if (buf == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_lua_escape_log(buf, p, len);\n        buf[buf_len - 1] = '\\0';\n        return luaL_error(L, \"unsafe byte \\\"0x%02x\\\" in redirect uri \\\"%s\\\"\",\n                          byte, buf);\n    }\n\n    uri = ngx_palloc(r->pool, len);\n    if (uri == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    ngx_memcpy(uri, p, len);\n\n    h = ngx_list_push(&r->headers_out.headers);\n    if (h == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    h->hash = ngx_http_lua_location_hash;\n\n#if 0\n    dd(\"location hash: %lu == %lu\",\n       (unsigned long) h->hash,\n       (unsigned long) ngx_hash_key_lc((u_char *) \"Location\",\n                                       sizeof(\"Location\") - 1));\n#endif\n\n    h->value.len = len;\n    h->value.data = uri;\n#if defined(nginx_version) && nginx_version >= 1023000\n    h->next = NULL;\n#endif\n    ngx_str_set(&h->key, \"Location\");\n\n    r->headers_out.status = rc;\n\n    ctx->exit_code = rc;\n    ctx->exited = 1;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua redirect to \\\"%V\\\" with code %i\",\n                   &h->value, ctx->exit_code);\n\n    if (len && uri[0] != '/') {\n        r->headers_out.location = h;\n    }\n\n    /*\n     * we do not set r->headers_out.location here to avoid the handling\n     * the local redirects without a host name by ngx_http_header_filter()\n     */\n\n    return lua_yield(L, 0);\n}\n\n\nstatic int\nngx_http_lua_on_abort(lua_State *L)\n{\n    int                           co_ref;\n    ngx_http_request_t           *r;\n    ngx_http_lua_ctx_t           *ctx;\n    ngx_http_lua_co_ctx_t        *coctx = NULL;\n    ngx_http_lua_loc_conf_t      *llcf;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    ngx_http_lua_check_fake_request2(L, r, ctx);\n\n    if (ctx->on_abort_co_ctx) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"duplicate call\");\n        return 2;\n    }\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n    if (!llcf->check_client_abort) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"lua_check_client_abort is off\");\n        return 2;\n    }\n\n    ngx_http_lua_coroutine_create_helper(L, r, ctx, &coctx, &co_ref);\n\n    coctx->co_ref = co_ref;\n    coctx->is_uthread = 1;\n    ctx->on_abort_co_ctx = coctx;\n\n    dd(\"on_wait thread 2: %p\", coctx->co);\n\n    coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED;\n    coctx->parent_co_ctx = ctx->cur_co_ctx;\n\n    lua_pushinteger(L, 1);\n    return 1;\n}\n\n\nint\nngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err,\n    size_t *errlen)\n{\n    ngx_http_lua_ctx_t       *ctx;\n\n    if (status == NGX_AGAIN || status == NGX_DONE) {\n        *errlen = ngx_snprintf(err, *errlen,\n                               \"bad argument to 'ngx.exit': does not accept \"\n                               \"NGX_AGAIN or NGX_DONE\")\n                  - err;\n        return NGX_ERROR;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        *errlen = ngx_snprintf(err, *errlen, \"no request ctx found\") - err;\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_lua_ffi_check_context(ctx, NGX_HTTP_LUA_CONTEXT_REWRITE\n                                       | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE\n                                       | NGX_HTTP_LUA_CONTEXT_ACCESS\n                                       | NGX_HTTP_LUA_CONTEXT_PRECONTENT\n                                       | NGX_HTTP_LUA_CONTEXT_CONTENT\n                                       | NGX_HTTP_LUA_CONTEXT_TIMER\n                                       | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER\n                                       | NGX_HTTP_LUA_CONTEXT_BALANCER\n#if HAVE_LUA_PROXY_SSL\n                                       | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT\n                                       | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY\n#endif\n                                       | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO\n                                       | NGX_HTTP_LUA_CONTEXT_SSL_CERT\n                                       | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE\n                                       | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH,\n                                       err, errlen)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT\n#if HAVE_LUA_PROXY_SSL\n                        | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT\n                        | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY\n#endif\n                        | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO\n                        | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE\n                        | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH))\n    {\n\n#if (NGX_HTTP_SSL)\n\n        ctx->exit_code = status;\n        ctx->exited = 1;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua exit with code %d\", status);\n\n        if (ctx->context == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE) {\n            return NGX_DONE;\n        }\n\n        return NGX_OK;\n\n#else\n\n        return NGX_ERROR;\n\n#endif\n    }\n\n    if (ctx->no_abort\n        && status != NGX_ERROR\n        && status != NGX_HTTP_CLOSE\n        && status != NGX_HTTP_REQUEST_TIME_OUT\n        && status != NGX_HTTP_CLIENT_CLOSED_REQUEST)\n    {\n        *errlen = ngx_snprintf(err, *errlen,\n                               \"attempt to abort with pending subrequests\")\n                  - err;\n        return NGX_ERROR;\n    }\n\n    if ((r->header_sent || ctx->header_sent)\n        && status >= NGX_HTTP_SPECIAL_RESPONSE\n        && status != NGX_HTTP_REQUEST_TIME_OUT\n        && status != NGX_HTTP_CLIENT_CLOSED_REQUEST\n        && status != NGX_HTTP_CLOSE)\n    {\n        if (status != (ngx_int_t) r->headers_out.status) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \"attempt to \"\n                          \"set status %d via ngx.exit after sending out the \"\n                          \"response status %ui\", status,\n                          r->headers_out.status);\n        }\n\n        status = NGX_HTTP_OK;\n    }\n\n    ctx->exit_code = status;\n    ctx->exited = 1;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua exit with code %i\", ctx->exit_code);\n\n    if (ctx->context & (NGX_HTTP_LUA_CONTEXT_HEADER_FILTER\n                        | NGX_HTTP_LUA_CONTEXT_BALANCER))\n    {\n        return NGX_DONE;\n    }\n\n    return NGX_OK;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_control.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_CONTROL_H_INCLUDED_\n#define _NGX_HTTP_LUA_CONTROL_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_control_api(ngx_log_t *log, lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_CONTROL_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_coroutine.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_coroutine.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_probe.h\"\n\n\n/*\n * Design:\n *\n * In order to support using ngx.* API in Lua coroutines, we have to create\n * new coroutine in the main coroutine instead of the calling coroutine\n */\n\n\nstatic int ngx_http_lua_coroutine_create(lua_State *L);\nstatic int ngx_http_lua_coroutine_wrap(lua_State *L);\nstatic int ngx_http_lua_coroutine_resume(lua_State *L);\nstatic int ngx_http_lua_coroutine_yield(lua_State *L);\nstatic int ngx_http_lua_coroutine_status(lua_State *L);\n\n\nstatic const ngx_str_t\n    ngx_http_lua_co_status_names[] =\n    {\n        ngx_string(\"running\"),\n        ngx_string(\"suspended\"),\n        ngx_string(\"normal\"),\n        ngx_string(\"dead\"),\n        ngx_string(\"zombie\")\n    };\n\n\n\nstatic int\nngx_http_lua_coroutine_create(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    return ngx_http_lua_coroutine_create_helper(L, r, ctx, NULL, NULL);\n}\n\n\nstatic int\nngx_http_lua_coroutine_wrap_runner(lua_State *L)\n{\n    /* retrieve closure and insert it at the bottom of\n     * the stack for coroutine.resume() */\n    lua_pushvalue(L, lua_upvalueindex(1));\n    lua_insert(L, 1);\n\n    return ngx_http_lua_coroutine_resume(L);\n}\n\n\nstatic int\nngx_http_lua_coroutine_wrap(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx = NULL;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    ngx_http_lua_coroutine_create_helper(L, r, ctx, &coctx, NULL);\n\n    coctx->is_wrap = 1;\n\n    lua_pushcclosure(L, ngx_http_lua_coroutine_wrap_runner, 1);\n\n    return 1;\n}\n\n\nint\nngx_http_lua_coroutine_create_helper(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t **pcoctx, int *co_ref)\n{\n    lua_State                     *vm;  /* the Lua VM */\n    lua_State                     *co;  /* new coroutine to be created */\n    ngx_http_lua_co_ctx_t         *coctx; /* co ctx for the new coroutine */\n    ngx_http_lua_main_conf_t      *lmcf;\n\n    luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,\n                  \"Lua function expected\");\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    vm = ngx_http_lua_get_lua_vm(r, ctx);\n\n    /* create new coroutine on root Lua state, so it always yields\n     * to main Lua thread\n     */\n    if (co_ref == NULL) {\n        co = lua_newthread(vm);\n\n    } else {\n        lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n        *co_ref = ngx_http_lua_new_cached_thread(vm, &co, lmcf, 0);\n    }\n\n    ngx_http_lua_probe_user_coroutine_create(r, L, co);\n\n    coctx = ngx_http_lua_get_co_ctx(co, ctx);\n    if (coctx == NULL) {\n        coctx = ngx_http_lua_create_co_ctx(r, ctx);\n        if (coctx == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n    } else {\n        ngx_memzero(coctx, sizeof(ngx_http_lua_co_ctx_t));\n        coctx->next_zombie_child_thread = &coctx->zombie_child_threads;\n        coctx->co_ref = LUA_NOREF;\n    }\n\n    coctx->co = co;\n    coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED;\n\n#ifdef OPENRESTY_LUAJIT\n    ngx_http_lua_set_req(co, r);\n    ngx_http_lua_attach_co_ctx_to_L(co, coctx);\n#else\n    /* make new coroutine share globals of the parent coroutine.\n     * NOTE: globals don't have to be separated! */\n    ngx_http_lua_get_globals_table(L);\n    lua_xmove(L, co, 1);\n    ngx_http_lua_set_globals_table(co);\n#endif\n\n    lua_xmove(vm, L, 1);    /* move coroutine from main thread to L */\n\n    if (co_ref) {\n        lua_pop(vm, 1);  /* pop coroutines */\n    }\n\n    lua_pushvalue(L, 1);    /* copy entry function to top of L*/\n    lua_xmove(L, co, 1);    /* move entry function from L to co */\n\n    if (pcoctx) {\n        *pcoctx = coctx;\n    }\n\n#ifdef NGX_LUA_USE_ASSERT\n    coctx->co_top = 1;\n#endif\n\n    return 1;    /* return new coroutine to Lua */\n}\n\n\nstatic int\nngx_http_lua_coroutine_resume(lua_State *L)\n{\n    lua_State                   *co;\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx;\n    ngx_http_lua_co_ctx_t       *p_coctx; /* parent co ctx */\n\n    co = lua_tothread(L, 1);\n\n    luaL_argcheck(L, co, 1, \"coroutine expected\");\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    p_coctx = ctx->cur_co_ctx;\n    if (p_coctx == NULL) {\n        return luaL_error(L, \"no parent co ctx found\");\n    }\n\n    coctx = ngx_http_lua_get_co_ctx(co, ctx);\n    if (coctx == NULL) {\n        return luaL_error(L, \"no co ctx found\");\n    }\n\n    ngx_http_lua_probe_user_coroutine_resume(r, L, co);\n\n    if (coctx->co_status != NGX_HTTP_LUA_CO_SUSPENDED) {\n        dd(\"coroutine resume: %d\", coctx->co_status);\n\n        lua_pushboolean(L, 0);\n        lua_pushfstring(L, \"cannot resume %s coroutine\",\n                        ngx_http_lua_co_status_names[coctx->co_status].data);\n        return 2;\n    }\n\n    p_coctx->co_status = NGX_HTTP_LUA_CO_NORMAL;\n\n    coctx->parent_co_ctx = p_coctx;\n\n    dd(\"set coroutine to running\");\n    coctx->co_status = NGX_HTTP_LUA_CO_RUNNING;\n\n    ctx->co_op = NGX_HTTP_LUA_USER_CORO_RESUME;\n    ctx->cur_co_ctx = coctx;\n\n    /* yield and pass args to main thread, and resume target coroutine from\n     * there */\n    return lua_yield(L, lua_gettop(L) - 1);\n}\n\n\nstatic int\nngx_http_lua_coroutine_yield(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    coctx = ctx->cur_co_ctx;\n\n    coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED;\n\n    ctx->co_op = NGX_HTTP_LUA_USER_CORO_YIELD;\n\n    if (!coctx->is_uthread && coctx->parent_co_ctx) {\n        dd(\"set coroutine to running\");\n        coctx->parent_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING;\n\n        ngx_http_lua_probe_user_coroutine_yield(r, coctx->parent_co_ctx->co, L);\n\n    } else {\n        ngx_http_lua_probe_user_coroutine_yield(r, NULL, L);\n    }\n\n    /* yield and pass retvals to main thread,\n     * and resume parent coroutine there */\n    return lua_yield(L, lua_gettop(L));\n}\n\n\nvoid\nngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)\n{\n    int         rc;\n\n    /* new coroutine table */\n    lua_createtable(L, 0 /* narr */, 16 /* nrec */);\n\n    /* get old coroutine table */\n    lua_getglobal(L, \"coroutine\");\n\n    /* set running to the old one */\n    lua_getfield(L, -1, \"running\");\n    lua_setfield(L, -3, \"running\");\n\n    lua_getfield(L, -1, \"create\");\n    lua_setfield(L, -3, \"_create\");\n\n    lua_getfield(L, -1, \"wrap\");\n    lua_setfield(L, -3, \"_wrap\");\n\n    lua_getfield(L, -1, \"resume\");\n    lua_setfield(L, -3, \"_resume\");\n\n    lua_getfield(L, -1, \"yield\");\n    lua_setfield(L, -3, \"_yield\");\n\n    lua_getfield(L, -1, \"status\");\n    lua_setfield(L, -3, \"_status\");\n\n    /* pop the old coroutine */\n    lua_pop(L, 1);\n\n    lua_pushcfunction(L, ngx_http_lua_coroutine_create);\n    lua_setfield(L, -2, \"__create\");\n\n    lua_pushcfunction(L, ngx_http_lua_coroutine_wrap);\n    lua_setfield(L, -2, \"__wrap\");\n\n    lua_pushcfunction(L, ngx_http_lua_coroutine_resume);\n    lua_setfield(L, -2, \"__resume\");\n\n    lua_pushcfunction(L, ngx_http_lua_coroutine_yield);\n    lua_setfield(L, -2, \"__yield\");\n\n    lua_pushcfunction(L, ngx_http_lua_coroutine_status);\n    lua_setfield(L, -2, \"__status\");\n\n    lua_setglobal(L, \"coroutine\");\n\n    /* inject coroutine APIs */\n    {\n        const char buf[] =\n            \"local keys = {'create', 'yield', 'resume', 'status', 'wrap'}\\n\"\n#ifdef OPENRESTY_LUAJIT\n            \"local get_req = require 'thread.exdata'\\n\"\n#else\n            \"local getfenv = getfenv\\n\"\n#endif\n            \"for _, key in ipairs(keys) do\\n\"\n               \"local std = coroutine['_' .. key]\\n\"\n               \"local ours = coroutine['__' .. key]\\n\"\n               \"local raw_ctx = ngx._phase_ctx\\n\"\n               \"coroutine[key] = function (...)\\n\"\n#ifdef OPENRESTY_LUAJIT\n                    \"local r = get_req()\\n\"\n#else\n                    \"local r = getfenv(0).__ngx_req\\n\"\n#endif\n                    \"if r ~= nil then\\n\"\n#ifdef OPENRESTY_LUAJIT\n                        \"local ctx = raw_ctx()\\n\"\n#else\n                        \"local ctx = raw_ctx(r)\\n\"\n#endif\n                        /* ignore header and body filters */\n                        \"if ctx ~= 0x020 and ctx ~= 0x040 then\\n\"\n                            \"return ours(...)\\n\"\n                        \"end\\n\"\n                    \"end\\n\"\n                    \"return std(...)\\n\"\n                \"end\\n\"\n            \"end\\n\"\n            \"package.loaded.coroutine = coroutine\"\n#if 0\n            \"debug.sethook(function () collectgarbage() end, 'rl', 1)\"\n#endif\n            ;\n\n        rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, \"=coroutine_api\");\n    }\n\n    if (rc != 0) {\n        ngx_log_error(NGX_LOG_ERR, log, 0,\n                      \"failed to load Lua code for coroutine_api: %i: %s\",\n                      rc, lua_tostring(L, -1));\n\n        lua_pop(L, 1);\n        return;\n    }\n\n    rc = lua_pcall(L, 0, 0, 0);\n    if (rc != 0) {\n        ngx_log_error(NGX_LOG_ERR, log, 0,\n                      \"failed to run the Lua code for coroutine_api: %i: %s\",\n                      rc, lua_tostring(L, -1));\n        lua_pop(L, 1);\n    }\n}\n\n\nstatic int\nngx_http_lua_coroutine_status(lua_State *L)\n{\n    lua_State                     *co;  /* new coroutine to be created */\n    ngx_http_request_t            *r;\n    ngx_http_lua_ctx_t            *ctx;\n    ngx_http_lua_co_ctx_t         *coctx; /* co ctx for the new coroutine */\n\n    co = lua_tothread(L, 1);\n\n    luaL_argcheck(L, co, 1, \"coroutine expected\");\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    coctx = ngx_http_lua_get_co_ctx(co, ctx);\n    if (coctx == NULL) {\n        lua_pushlstring(L, (const char *)\n                        ngx_http_lua_co_status_names[NGX_HTTP_LUA_CO_DEAD].data,\n                        ngx_http_lua_co_status_names[NGX_HTTP_LUA_CO_DEAD].len);\n        return 1;\n    }\n\n    dd(\"co status: %d\", coctx->co_status);\n\n    lua_pushlstring(L, (const char *)\n                    ngx_http_lua_co_status_names[coctx->co_status].data,\n                    ngx_http_lua_co_status_names[coctx->co_status].len);\n    return 1;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_coroutine.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_COROUTINE_H_INCLUDED_\n#define _NGX_HTTP_LUA_COROUTINE_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L);\n\nint ngx_http_lua_coroutine_create_helper(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t **pcoctx, int *co_ref);\n\n\n#endif /* _NGX_HTTP_LUA_COROUTINE_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_ctx.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_ssl.h\"\n#include \"ngx_http_lua_ctx.h\"\n\n\ntypedef struct {\n    int              ref;\n    lua_State       *vm;\n} ngx_http_lua_ngx_ctx_cleanup_data_t;\n\n\nstatic ngx_int_t ngx_http_lua_ngx_ctx_add_cleanup(ngx_http_request_t *r,\n    ngx_pool_t *pool, int ref);\nstatic void ngx_http_lua_ngx_ctx_cleanup(void *data);\n\n\nint\nngx_http_lua_ngx_set_ctx_helper(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, int index)\n{\n    ngx_pool_t              *pool;\n\n    if (index < 0) {\n        index = lua_gettop(L) + index + 1;\n    }\n\n    if (ctx->ctx_ref == LUA_NOREF) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua create ngx.ctx table for the current request\");\n\n        lua_pushliteral(L, ngx_http_lua_ctx_tables_key);\n        lua_rawget(L, LUA_REGISTRYINDEX);\n        lua_pushvalue(L, index);\n        ctx->ctx_ref = luaL_ref(L, -2);\n        lua_pop(L, 1);\n\n        pool = r->pool;\n        if (ngx_http_lua_ngx_ctx_add_cleanup(r, pool, ctx->ctx_ref) != NGX_OK) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        return 0;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua fetching existing ngx.ctx table for the current \"\n                   \"request\");\n\n    lua_pushliteral(L, ngx_http_lua_ctx_tables_key);\n    lua_rawget(L, LUA_REGISTRYINDEX);\n    luaL_unref(L, -1, ctx->ctx_ref);\n    lua_pushvalue(L, index);\n    ctx->ctx_ref = luaL_ref(L, -2);\n    lua_pop(L, 1);\n\n    return 0;\n}\n\n\nint\nngx_http_lua_ffi_get_ctx_ref(ngx_http_request_t *r, int *in_ssl_phase,\n    int *ssl_ctx_ref)\n{\n    ngx_http_lua_ctx_t              *ctx;\n#if (NGX_HTTP_SSL)\n    ngx_http_lua_ssl_ctx_t          *ssl_ctx;\n#endif\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_HTTP_LUA_FFI_NO_REQ_CTX;\n    }\n\n    if (ctx->ctx_ref >= 0 || in_ssl_phase == NULL) {\n        return ctx->ctx_ref;\n    }\n\n    *in_ssl_phase = ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT\n                                    | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO\n                                    | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH\n                                    | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE);\n    *ssl_ctx_ref = LUA_NOREF;\n\n#if (NGX_HTTP_SSL)\n    if (r->connection->ssl != NULL) {\n        ssl_ctx = ngx_http_lua_ssl_get_ctx(r->connection->ssl->connection);\n\n        if (ssl_ctx != NULL) {\n            *ssl_ctx_ref = ssl_ctx->ctx_ref;\n        }\n    }\n#endif\n\n    return LUA_NOREF;\n}\n\n\nint\nngx_http_lua_ffi_set_ctx_ref(ngx_http_request_t *r, int ref)\n{\n    ngx_pool_t                      *pool;\n    ngx_http_lua_ctx_t              *ctx;\n#if (NGX_HTTP_SSL)\n    ngx_connection_t                *c;\n    ngx_http_lua_ssl_ctx_t          *ssl_ctx;\n#endif\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_HTTP_LUA_FFI_NO_REQ_CTX;\n    }\n\n#if (NGX_HTTP_SSL)\n    if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT\n                        | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO\n                        | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH\n                        | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE))\n    {\n        ssl_ctx = ngx_http_lua_ssl_get_ctx(r->connection->ssl->connection);\n        if (ssl_ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        ssl_ctx->ctx_ref = ref;\n        c = ngx_ssl_get_connection(r->connection->ssl->connection);\n        pool = c->pool;\n\n    } else {\n        pool = r->pool;\n    }\n\n#else\n    pool = r->pool;\n#endif\n\n    ctx->ctx_ref = ref;\n\n    if (ngx_http_lua_ngx_ctx_add_cleanup(r, pool, ref) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_ngx_ctx_add_cleanup(ngx_http_request_t *r, ngx_pool_t *pool,\n    int ref)\n{\n    lua_State                   *L;\n    ngx_pool_cleanup_t          *cln;\n    ngx_http_lua_ctx_t          *ctx;\n\n    ngx_http_lua_ngx_ctx_cleanup_data_t    *data;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    L = ngx_http_lua_get_lua_vm(r, ctx);\n\n    cln = ngx_pool_cleanup_add(pool,\n                               sizeof(ngx_http_lua_ngx_ctx_cleanup_data_t));\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_http_lua_ngx_ctx_cleanup;\n\n    data = cln->data;\n    data->vm = L;\n    data->ref = ref;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_lua_ngx_ctx_cleanup(void *data)\n{\n    lua_State       *L;\n\n    ngx_http_lua_ngx_ctx_cleanup_data_t    *clndata = data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua release ngx.ctx at ref %d\", clndata->ref);\n\n    L = clndata->vm;\n\n    lua_pushliteral(L, ngx_http_lua_ctx_tables_key);\n    lua_rawget(L, LUA_REGISTRYINDEX);\n    luaL_unref(L, -1, clndata->ref);\n    lua_pop(L, 1);\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_ctx.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_CTX_H_INCLUDED_\n#define _NGX_HTTP_LUA_CTX_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nint ngx_http_lua_ngx_set_ctx_helper(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, int index);\n\n\n#endif /* _NGX_HTTP_LUA_CTX_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_directive.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_common.h\"\n#include \"ngx_http_lua_directive.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_precontentby.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_accessby.h\"\n#include \"ngx_http_lua_server_rewriteby.h\"\n#include \"ngx_http_lua_rewriteby.h\"\n#include \"ngx_http_lua_logby.h\"\n#include \"ngx_http_lua_headerfilterby.h\"\n#include \"ngx_http_lua_bodyfilterby.h\"\n#include \"ngx_http_lua_initby.h\"\n#include \"ngx_http_lua_initworkerby.h\"\n#include \"ngx_http_lua_exitworkerby.h\"\n#include \"ngx_http_lua_shdict.h\"\n#include \"ngx_http_lua_ssl_certby.h\"\n#include \"ngx_http_lua_lex.h\"\n#include \"api/ngx_http_lua_api.h\"\n#include \"ngx_http_lua_log_ringbuf.h\"\n#include \"ngx_http_lua_log.h\"\n\n\n/* the max length is 60, after deducting the fixed four characters \"=(:)\"\n * only 56 left.\n */\n#define LJ_CHUNKNAME_MAX_LEN 56\n\n\ntypedef struct ngx_http_lua_block_parser_ctx_s\n    ngx_http_lua_block_parser_ctx_t;\n\n\n#if defined(NDK) && NDK\n#include \"ngx_http_lua_setby.h\"\n\n\nstatic ngx_int_t ngx_http_lua_set_by_lua_init(ngx_http_request_t *r);\n#endif\n\nstatic ngx_int_t ngx_http_lua_conf_read_lua_token(ngx_conf_t *cf,\n    ngx_http_lua_block_parser_ctx_t *ctx);\nstatic u_char *ngx_http_lua_strlstrn(u_char *s1, u_char *last, u_char *s2,\n    size_t n);\n\n\nstruct ngx_http_lua_block_parser_ctx_s {\n    ngx_uint_t  start_line;\n    int         token_len;\n};\n\n\nenum {\n    FOUND_LEFT_CURLY = 0,\n    FOUND_RIGHT_CURLY,\n    FOUND_LEFT_LBRACKET_STR,\n    FOUND_LBRACKET_STR = FOUND_LEFT_LBRACKET_STR,\n    FOUND_LEFT_LBRACKET_CMT,\n    FOUND_LBRACKET_CMT = FOUND_LEFT_LBRACKET_CMT,\n    FOUND_RIGHT_LBRACKET,\n    FOUND_COMMENT_LINE,\n    FOUND_DOUBLE_QUOTED,\n    FOUND_SINGLE_QUOTED,\n};\n\n\nchar *\nngx_http_lua_shared_dict(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_lua_main_conf_t   *lmcf = conf;\n\n    ngx_str_t                  *value, name;\n    ngx_shm_zone_t             *zone;\n    ngx_shm_zone_t            **zp;\n    ngx_http_lua_shdict_ctx_t  *ctx;\n    ssize_t                     size;\n\n    if (lmcf->shdict_zones == NULL) {\n        lmcf->shdict_zones = ngx_palloc(cf->pool, sizeof(ngx_array_t));\n        if (lmcf->shdict_zones == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_array_init(lmcf->shdict_zones, cf->pool, 2,\n                           sizeof(ngx_shm_zone_t *))\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    value = cf->args->elts;\n\n    ctx = NULL;\n\n    if (value[1].len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid lua shared dict name \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    name = value[1];\n\n    size = ngx_parse_size(&value[2]);\n\n    if (size <= 8191) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid lua shared dict size \\\"%V\\\"\", &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_lua_shdict_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx->name = name;\n    ctx->main_conf = lmcf;\n    ctx->log = &cf->cycle->new_log;\n\n    zone = ngx_http_lua_shared_memory_add(cf, &name, (size_t) size,\n                                          &ngx_http_lua_module);\n    if (zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (zone->data) {\n        ctx = zone->data;\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"lua_shared_dict \\\"%V\\\" is already defined as \"\n                           \"\\\"%V\\\"\", &name, &ctx->name);\n        return NGX_CONF_ERROR;\n    }\n\n    zone->init = ngx_http_lua_shdict_init_zone;\n    zone->data = ctx;\n\n    zp = ngx_array_push(lmcf->shdict_zones);\n    if (zp == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *zp = zone;\n\n    lmcf->requires_shm = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char             *p = conf;\n    ngx_flag_t       *fp;\n    char             *ret;\n\n    ret = ngx_conf_set_flag_slot(cf, cmd, conf);\n    if (ret != NGX_CONF_OK) {\n        return ret;\n    }\n\n    fp = (ngx_flag_t *) (p + cmd->offset);\n\n    if (!*fp) {\n        ngx_conf_log_error(NGX_LOG_ALERT, cf, 0,\n                           \"lua_code_cache is off; this will hurt \"\n                           \"performance\");\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_load_resty_core(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                       \"lua_load_resty_core is deprecated (the lua-resty-core \"\n                       \"library is required since ngx_lua v0.10.16)\");\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_package_cpath(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_lua_main_conf_t *lmcf = conf;\n    ngx_str_t                *value;\n\n    if (lmcf->lua_cpath.len != 0) {\n        return \"is duplicate\";\n    }\n\n    dd(\"enter\");\n\n    value = cf->args->elts;\n\n    lmcf->lua_cpath.len = value[1].len;\n    lmcf->lua_cpath.data = value[1].data;\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_package_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_lua_main_conf_t *lmcf = conf;\n    ngx_str_t                *value;\n\n    if (lmcf->lua_path.len != 0) {\n        return \"is duplicate\";\n    }\n\n    dd(\"enter\");\n\n    value = cf->args->elts;\n\n    lmcf->lua_path.len = value[1].len;\n    lmcf->lua_path.data = value[1].data;\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_regex_cache_max_entries(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n#if (NGX_PCRE)\n    return ngx_conf_set_num_slot(cf, cmd, conf);\n#else\n    return NGX_CONF_OK;\n#endif\n}\n\n\nchar *\nngx_http_lua_regex_match_limit(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n#if (NGX_PCRE)\n    return ngx_conf_set_num_slot(cf, cmd, conf);\n#else\n    return NGX_CONF_OK;\n#endif\n}\n\n\n#if defined(NDK) && NDK\nchar *\nngx_http_lua_set_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_set_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_set_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    size_t               chunkname_len;\n    u_char              *chunkname;\n    u_char              *cache_key;\n    ngx_str_t           *value;\n    ngx_str_t            target;\n    ndk_set_var_t        filter;\n\n    ngx_http_lua_set_var_data_t     *filter_data;\n\n    /*\n     * value[0] = \"set_by_lua\"\n     * value[1] = target variable name\n     * value[2] = lua script source to be executed\n     * value[3..] = real params\n     * */\n    value = cf->args->elts;\n    target = value[1];\n\n    filter.type = NDK_SET_VAR_MULTI_VALUE_DATA;\n    filter.func = cmd->post;\n    filter.size = cf->args->nelts - 3;    /*  get number of real params */\n\n    filter_data = ngx_palloc(cf->pool, sizeof(ngx_http_lua_set_var_data_t));\n    if (filter_data == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    cache_key = ngx_http_lua_gen_chunk_cache_key(cf, \"set_by_lua\",\n                                                 value[2].data,\n                                                 value[2].len);\n    if (cache_key == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    chunkname = ngx_http_lua_gen_chunk_name(cf, \"set_by_lua\",\n                                            sizeof(\"set_by_lua\") - 1,\n                                            &chunkname_len);\n    if (chunkname == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    filter_data->key = cache_key;\n    filter_data->chunkname = chunkname;\n    filter_data->ref = LUA_REFNIL;\n    filter_data->script = value[2];\n    filter_data->size = filter.size;\n\n    filter.data = filter_data;\n\n    return ndk_set_var_multi_value_core(cf, &target, &value[3], &filter);\n}\n\n\nchar *\nngx_http_lua_set_by_lua_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    u_char              *cache_key = NULL;\n    ngx_str_t           *value;\n    ngx_str_t            target;\n    ndk_set_var_t        filter;\n\n    ngx_http_lua_set_var_data_t           *filter_data;\n    ngx_http_complex_value_t               cv;\n    ngx_http_compile_complex_value_t       ccv;\n\n    /*\n     * value[0] = \"set_by_lua_file\"\n     * value[1] = target variable name\n     * value[2] = lua script file path to be executed\n     * value[3..] = real params\n     * */\n    value = cf->args->elts;\n    target = value[1];\n\n    filter.type = NDK_SET_VAR_MULTI_VALUE_DATA;\n    filter.func = cmd->post;\n    filter.size = cf->args->nelts - 2;    /*  get number of real params and\n                                              lua script */\n\n    filter_data = ngx_palloc(cf->pool, sizeof(ngx_http_lua_set_var_data_t));\n    if (filter_data == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n    ccv.cf = cf;\n    ccv.value = &value[2];\n    ccv.complex_value = &cv;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (cv.lengths == NULL) {\n        /* no variable found */\n        cache_key = ngx_http_lua_gen_file_cache_key(cf, value[2].data,\n                                                    value[2].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    filter_data->key = cache_key;\n    filter_data->ref = LUA_REFNIL;\n    filter_data->size = filter.size;\n    filter_data->chunkname = NULL;\n\n    ngx_str_null(&filter_data->script);\n\n    filter.data = filter_data;\n\n    return ndk_set_var_multi_value_core(cf, &target, &value[2], &filter);\n}\n\n\nngx_int_t\nngx_http_lua_filter_set_by_lua_inline(ngx_http_request_t *r, ngx_str_t *val,\n    ngx_http_variable_value_t *v, void *data)\n{\n    lua_State                   *L;\n    ngx_int_t                    rc;\n\n    ngx_http_lua_set_var_data_t     *filter_data = data;\n\n    if (ngx_http_lua_set_by_lua_init(r) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    /*  load Lua inline script (w/ cache)        sp = 1 */\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       filter_data->script.data,\n                                       filter_data->script.len,\n                                       &filter_data->ref,\n                                       filter_data->key,\n                                       (const char *) filter_data->chunkname);\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_http_lua_set_by_chunk(L, r, val, v, filter_data->size,\n                                   &filter_data->script);\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_lua_filter_set_by_lua_file(ngx_http_request_t *r, ngx_str_t *val,\n    ngx_http_variable_value_t *v, void *data)\n{\n    lua_State                   *L;\n    ngx_int_t                    rc;\n    u_char                      *script_path;\n    size_t                       nargs;\n\n    ngx_http_lua_set_var_data_t     *filter_data = data;\n\n    dd(\"set by lua file\");\n\n    if (ngx_http_lua_set_by_lua_init(r) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    filter_data->script.data = v[0].data;\n    filter_data->script.len = v[0].len;\n\n    /* skip the lua file path argument */\n    v++;\n    nargs = filter_data->size - 1;\n\n    dd(\"script: %.*s\", (int) filter_data->script.len, filter_data->script.data);\n    dd(\"nargs: %d\", (int) nargs);\n\n    script_path = ngx_http_lua_rebase_path(r->pool, filter_data->script.data,\n                                           filter_data->script.len);\n    if (script_path == NULL) {\n        return NGX_ERROR;\n    }\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    /*  load Lua script file (w/ cache)        sp = 1 */\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,\n                                     &filter_data->ref,\n                                     filter_data->key);\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_http_lua_set_by_chunk(L, r, val, v, nargs, &filter_data->script);\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n#endif /* defined(NDK) && NDK */\n\n\nchar *\nngx_http_lua_rewrite_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_rewrite_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    size_t                       chunkname_len;\n    u_char                      *cache_key = NULL, *chunkname;\n    ngx_str_t                   *value;\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_http_lua_loc_conf_t     *llcf = conf;\n\n    ngx_http_compile_complex_value_t         ccv;\n\n    dd(\"enter\");\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (llcf->rewrite_handler) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (value[1].len == 0) {\n        /*  Oops...Invalid location conf */\n        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,\n                           \"invalid location config: no runnable Lua code\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (cmd->post == ngx_http_lua_rewrite_handler_inline) {\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"rewrite_by_lua\",\n                                                sizeof(\"rewrite_by_lua\") - 1,\n                                                &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf, \"rewrite_by_lua\",\n                                                     value[1].data,\n                                                     value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        llcf->rewrite_src.value = value[1];\n        llcf->rewrite_chunkname = chunkname;\n\n    } else {\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n        ccv.cf = cf;\n        ccv.value = &value[1];\n        ccv.complex_value = &llcf->rewrite_src;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (llcf->rewrite_src.lengths == NULL) {\n            /* no variable found */\n            cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                        value[1].len);\n            if (cache_key == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    llcf->rewrite_src_key = cache_key;\n    llcf->rewrite_handler = (ngx_http_handler_pt) cmd->post;\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n\n    lmcf->requires_rewrite = 1;\n    lmcf->requires_capture_filter = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_server_rewrite_by_lua_block(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n    save = *cf;\n    cf->handler = ngx_http_lua_server_rewrite_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_server_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    size_t                       chunkname_len;\n    u_char                      *cache_key = NULL, *chunkname;\n    ngx_str_t                   *value;\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_http_lua_srv_conf_t     *lscf = conf;\n\n    ngx_http_compile_complex_value_t         ccv;\n\n    dd(\"enter\");\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (lscf->srv.server_rewrite_handler) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (value[1].len == 0) {\n        /*  Oops...Invalid location conf */\n        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,\n                           \"invalid location config: no runnable Lua code\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (cmd->post == ngx_http_lua_server_rewrite_handler_inline) {\n        chunkname =\n            ngx_http_lua_gen_chunk_name(cf, \"server_rewrite_by_lua\",\n                                        sizeof(\"server_rewrite_by_lua\") - 1,\n                                        &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cache_key =\n            ngx_http_lua_gen_chunk_cache_key(cf, \"server_rewrite_by_lua\",\n                                             value[1].data,\n                                             value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        lscf->srv.server_rewrite_src.value = value[1];\n        lscf->srv.server_rewrite_chunkname = chunkname;\n\n    } else {\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n        ccv.cf = cf;\n        ccv.value = &value[1];\n        ccv.complex_value = &lscf->srv.server_rewrite_src;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (lscf->srv.server_rewrite_src.lengths == NULL) {\n            /* no variable found */\n            cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                        value[1].len);\n            if (cache_key == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    lscf->srv.server_rewrite_src_key = cache_key;\n    lscf->srv.server_rewrite_handler =\n                                  (ngx_http_lua_srv_conf_handler_pt) cmd->post;\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n\n    lmcf->requires_server_rewrite = 1;\n    lmcf->requires_capture_filter = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_access_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_access_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_access_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    size_t                       chunkname_len;\n    u_char                      *cache_key = NULL, *chunkname;\n    ngx_str_t                   *value;\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_http_lua_loc_conf_t     *llcf = conf;\n\n    ngx_http_compile_complex_value_t         ccv;\n\n    dd(\"enter\");\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (llcf->access_handler) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (value[1].len == 0) {\n        /*  Oops...Invalid location conf */\n        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,\n                           \"invalid location config: no runnable Lua code\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (cmd->post == ngx_http_lua_access_handler_inline) {\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"access_by_lua\",\n                                                sizeof(\"access_by_lua\") - 1,\n                                                &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf, \"access_by_lua\",\n                                                     value[1].data,\n                                                     value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        llcf->access_src.value = value[1];\n        llcf->access_chunkname = chunkname;\n\n    } else {\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n        ccv.cf = cf;\n        ccv.value = &value[1];\n        ccv.complex_value = &llcf->access_src;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (llcf->access_src.lengths == NULL) {\n            /* no variable found */\n            cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                        value[1].len);\n            if (cache_key == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    llcf->access_src_key = cache_key;\n    llcf->access_handler = (ngx_http_handler_pt) cmd->post;\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n\n    lmcf->requires_access = 1;\n    lmcf->requires_capture_filter = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_precontent_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_precontent_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_precontent_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    size_t                       chunkname_len;\n    u_char                      *cache_key = NULL, *chunkname;\n    ngx_str_t                   *value;\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_http_lua_loc_conf_t     *llcf = conf;\n\n    ngx_http_compile_complex_value_t         ccv;\n\n    dd(\"enter\");\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (llcf->precontent_handler) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (value[1].len == 0) {\n        /*  Oops...Invalid location conf */\n        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,\n                           \"invalid location config: no runnable Lua code\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (cmd->post == ngx_http_lua_precontent_handler_inline) {\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"precontent_by_lua\",\n                                                sizeof(\"precontent_by_lua\") - 1,\n                                                &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf, \"precontent_by_lua\",\n                                                     value[1].data,\n                                                     value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        llcf->precontent_src.value = value[1];\n        llcf->precontent_chunkname = chunkname;\n\n    } else {\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n        ccv.cf = cf;\n        ccv.value = &value[1];\n        ccv.complex_value = &llcf->precontent_src;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (llcf->precontent_src.lengths == NULL) {\n            /* no variable found */\n            cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                        value[1].len);\n            if (cache_key == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    llcf->precontent_src_key = cache_key;\n    llcf->precontent_handler = (ngx_http_handler_pt) cmd->post;\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n\n    lmcf->requires_precontent = 1;\n    lmcf->requires_capture_filter = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_content_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_content_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_content_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    size_t                       chunkname_len;\n    u_char                      *cache_key = NULL, *chunkname;\n    ngx_str_t                   *value;\n    ngx_http_core_loc_conf_t    *clcf;\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_http_lua_loc_conf_t     *llcf = conf;\n\n    ngx_http_compile_complex_value_t         ccv;\n\n    dd(\"enter\");\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (llcf->content_handler) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    dd(\"value[0]: %.*s\", (int) value[0].len, value[0].data);\n    dd(\"value[1]: %.*s\", (int) value[1].len, value[1].data);\n\n    if (value[1].len == 0) {\n        /*  Oops...Invalid location conf */\n        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,\n                           \"invalid location config: no runnable Lua code\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (cmd->post == ngx_http_lua_content_handler_inline) {\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"content_by_lua\",\n                                                sizeof(\"content_by_lua\") - 1,\n                                                &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf, \"content_by_lua\",\n                                                     value[1].data,\n                                                     value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        llcf->content_src.value = value[1];\n        llcf->content_chunkname = chunkname;\n\n    } else {\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n        ccv.cf = cf;\n        ccv.value = &value[1];\n        ccv.complex_value = &llcf->content_src;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (llcf->content_src.lengths == NULL) {\n            /* no variable found */\n            cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                        value[1].len);\n            if (cache_key == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    llcf->content_src_key = cache_key;\n    llcf->content_handler = (ngx_http_handler_pt) cmd->post;\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n\n    lmcf->requires_capture_filter = 1;\n\n    /*  register location content handler */\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    if (clcf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    clcf->handler = ngx_http_lua_content_handler;\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_log_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_log_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_log_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    size_t                       chunkname_len;\n    u_char                      *cache_key = NULL, *chunkname;\n    ngx_str_t                   *value;\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_http_lua_loc_conf_t     *llcf = conf;\n\n    ngx_http_compile_complex_value_t         ccv;\n\n    dd(\"enter\");\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (llcf->log_handler) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (value[1].len == 0) {\n        /*  Oops...Invalid location conf */\n        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,\n                           \"invalid location config: no runnable Lua code\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (cmd->post == ngx_http_lua_log_handler_inline) {\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"log_by_lua\",\n                                                sizeof(\"log_by_lua\") - 1,\n                                                &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf, \"log_by_lua\",\n                                                     value[1].data,\n                                                     value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        llcf->log_src.value = value[1];\n        llcf->log_chunkname = chunkname;\n\n    } else {\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n        ccv.cf = cf;\n        ccv.value = &value[1];\n        ccv.complex_value = &llcf->log_src;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (llcf->log_src.lengths == NULL) {\n            /* no variable found */\n            cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                        value[1].len);\n            if (cache_key == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    llcf->log_src_key = cache_key;\n    llcf->log_handler = (ngx_http_handler_pt) cmd->post;\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n\n    lmcf->requires_log = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_header_filter_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_header_filter_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_header_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    size_t                       chunkname_len;\n    u_char                      *cache_key = NULL, *chunkname;\n    ngx_str_t                   *value;\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_http_lua_loc_conf_t     *llcf = conf;\n\n    ngx_http_compile_complex_value_t         ccv;\n\n    dd(\"enter\");\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (llcf->header_filter_handler) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (value[1].len == 0) {\n        /*  Oops...Invalid location conf */\n        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,\n                           \"invalid location config: no runnable Lua code\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (cmd->post == ngx_http_lua_header_filter_inline) {\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf, \"header_filter_by_lua\",\n                                                     value[1].data,\n                                                     value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"header_filter_by_lua\",\n                            sizeof(\"header_filter_by_lua\") - 1, &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        llcf->header_filter_src.value = value[1];\n        llcf->header_filter_chunkname = chunkname;\n\n    } else {\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n        ccv.cf = cf;\n        ccv.value = &value[1];\n        ccv.complex_value = &llcf->header_filter_src;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (llcf->header_filter_src.lengths == NULL) {\n            /* no variable found */\n            cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                        value[1].len);\n            if (cache_key == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    llcf->header_filter_src_key = cache_key;\n    llcf->header_filter_handler = (ngx_http_handler_pt) cmd->post;\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n\n    lmcf->requires_header_filter = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_body_filter_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_body_filter_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_body_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    size_t                       chunkname_len;\n    u_char                      *cache_key = NULL, *chunkname;\n    ngx_str_t                   *value;\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_http_lua_loc_conf_t     *llcf = conf;\n\n    ngx_http_compile_complex_value_t         ccv;\n\n    dd(\"enter\");\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (llcf->body_filter_handler) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (value[1].len == 0) {\n        /*  Oops...Invalid location conf */\n        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,\n                           \"invalid location config: no runnable Lua code\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (cmd->post == ngx_http_lua_body_filter_inline) {\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf, \"body_filter_by_lua\",\n                                                     value[1].data,\n                                                     value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"body_filter_by_lua\",\n                              sizeof(\"body_filter_by_lua\") - 1, &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n\n        /* Don't eval nginx variables for inline lua code */\n        llcf->body_filter_src.value = value[1];\n        llcf->body_filter_chunkname = chunkname;\n\n    } else {\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n        ccv.cf = cf;\n        ccv.value = &value[1];\n        ccv.complex_value = &llcf->body_filter_src;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (llcf->body_filter_src.lengths == NULL) {\n            /* no variable found */\n            cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                        value[1].len);\n            if (cache_key == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    llcf->body_filter_src_key = cache_key;\n    llcf->body_filter_handler = (ngx_http_output_body_filter_pt) cmd->post;\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n\n    lmcf->requires_body_filter = 1;\n    lmcf->requires_header_filter = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_init_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_init_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_init_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    u_char                      *name;\n    ngx_str_t                   *value;\n    ngx_http_lua_main_conf_t    *lmcf = conf;\n    size_t                       chunkname_len;\n    u_char                      *chunkname;\n\n    dd(\"enter\");\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (lmcf->init_handler) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (value[1].len == 0) {\n        /*  Oops...Invalid location conf */\n        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,\n                           \"invalid location config: no runnable Lua code\");\n        return NGX_CONF_ERROR;\n    }\n\n    lmcf->init_handler = (ngx_http_lua_main_conf_handler_pt) cmd->post;\n\n    if (cmd->post == ngx_http_lua_init_by_file) {\n        name = ngx_http_lua_rebase_path(cf->pool, value[1].data,\n                                        value[1].len);\n        if (name == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        lmcf->init_src.data = name;\n        lmcf->init_src.len = ngx_strlen(name);\n\n    } else {\n        lmcf->init_src = value[1];\n\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"init_by_lua\",\n                                                sizeof(\"init_by_lua\") - 1,\n                                                &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        lmcf->init_chunkname = chunkname;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_init_worker_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_init_worker_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_init_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    u_char                      *name;\n    ngx_str_t                   *value;\n    ngx_http_lua_main_conf_t    *lmcf = conf;\n    size_t                       chunkname_len;\n    u_char                      *chunkname;\n\n    dd(\"enter\");\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (lmcf->init_worker_handler) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    lmcf->init_worker_handler = (ngx_http_lua_main_conf_handler_pt) cmd->post;\n\n    if (cmd->post == ngx_http_lua_init_worker_by_file) {\n        name = ngx_http_lua_rebase_path(cf->pool, value[1].data,\n                                        value[1].len);\n        if (name == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        lmcf->init_worker_src.data = name;\n        lmcf->init_worker_src.len = ngx_strlen(name);\n\n    } else {\n        lmcf->init_worker_src = value[1];\n\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"init_worker_by_lua\",\n                              sizeof(\"init_worker_by_lua\") - 1, &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        lmcf->init_worker_chunkname = chunkname;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_lua_exit_worker_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_exit_worker_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_exit_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    u_char                      *name;\n    ngx_str_t                   *value;\n    ngx_http_lua_main_conf_t    *lmcf = conf;\n    size_t                       chunkname_len;\n    u_char                      *chunkname;\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (lmcf->exit_worker_handler) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    lmcf->exit_worker_handler = (ngx_http_lua_main_conf_handler_pt) cmd->post;\n\n    if (cmd->post == ngx_http_lua_exit_worker_by_file) {\n        name = ngx_http_lua_rebase_path(cf->pool, value[1].data,\n                                        value[1].len);\n        if (name == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        lmcf->exit_worker_src.data = name;\n        lmcf->exit_worker_src.len = ngx_strlen(name);\n\n    } else {\n        lmcf->exit_worker_src = value[1];\n\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"exit_worker_by_lua\",\n                                                sizeof(\"exit_worker_by_lua\")- 1,\n                                                &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        lmcf->exit_worker_chunkname = chunkname;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\n#if defined(NDK) && NDK\nstatic ngx_int_t\nngx_http_lua_set_by_lua_init(ngx_http_request_t *r)\n{\n    lua_State                   *L;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_pool_cleanup_t          *cln;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n    } else {\n        L = ngx_http_lua_get_lua_vm(r, ctx);\n        ngx_http_lua_reset_ctx(r, L, ctx);\n    }\n\n    if (ctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n        if (cln == NULL) {\n            return NGX_ERROR;\n        }\n\n        cln->handler = ngx_http_lua_request_cleanup_handler;\n        cln->data = ctx;\n        ctx->cleanup = &cln->handler;\n    }\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_SET;\n    return NGX_OK;\n}\n#endif\n\n\nu_char *\nngx_http_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag, size_t tag_len,\n    size_t *chunkname_len)\n{\n    u_char      *p, *out;\n    size_t       len;\n    ngx_uint_t   start_line;\n    ngx_str_t   *conf_prefix;\n    ngx_str_t   *filename;\n    u_char      *filename_end;\n    const char  *pre_str = \"\";\n    ngx_uint_t   reserve_len;\n\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    len = sizeof(\"=(:)\") - 1 + tag_len + cf->conf_file->file.name.len\n          + NGX_INT64_LEN + 1;\n\n    out = ngx_palloc(cf->pool, len);\n    if (out == NULL) {\n        return NULL;\n    }\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n    start_line = lmcf->directive_line > 0\n        ? lmcf->directive_line : cf->conf_file->line;\n    p = ngx_snprintf(out, len, \"%d\", start_line);\n    reserve_len = tag_len + p - out;\n\n    filename = &cf->conf_file->file.name;\n    filename_end = filename->data + filename->len;\n    if (filename->len > 0) {\n        if (filename->len >= 11) {\n            p = filename_end - 11;\n            if ((*p == '/' || *p == '\\\\')\n                && ngx_memcmp(p, \"/nginx.conf\", 11) == 0)\n            {\n                p++; /* now p is nginx.conf */\n                goto found;\n            }\n        }\n\n        conf_prefix = &cf->cycle->conf_prefix;\n        p = filename->data + conf_prefix->len;\n        if ((conf_prefix->len < filename->len)\n            && ngx_memcmp(conf_prefix->data,\n                          filename->data, conf_prefix->len) == 0)\n        {\n            /* files in conf_prefix directory, use the relative path */\n            if (filename_end - p + reserve_len > LJ_CHUNKNAME_MAX_LEN) {\n                p = filename_end - LJ_CHUNKNAME_MAX_LEN + reserve_len + 3;\n                pre_str = \"...\";\n            }\n\n            goto found;\n        }\n    }\n\n    p = filename->data;\n\n    if (filename->len + reserve_len <= LJ_CHUNKNAME_MAX_LEN) {\n        goto found;\n    }\n\n    p = filename_end - LJ_CHUNKNAME_MAX_LEN + reserve_len + 3;\n    pre_str = \"...\";\n\nfound:\n\n\n    p = ngx_snprintf(out, len, \"=%*s(%s%*s:%d)%Z\",\n                     tag_len, tag, pre_str, filename_end - p,\n                     p, start_line);\n\n    *chunkname_len = p - out - 1;  /* exclude the trailing '\\0' byte */\n\n    return out;\n}\n\n\n/* a specialized version of the standard ngx_conf_parse() function */\nchar *\nngx_http_lua_conf_lua_block_parse(ngx_conf_t *cf, ngx_command_t *cmd)\n{\n    ngx_http_lua_main_conf_t           *lmcf;\n    ngx_http_lua_block_parser_ctx_t     ctx;\n\n    int               level = 1;\n    char             *rv;\n    u_char           *p;\n    size_t            len;\n    ngx_str_t        *src, *dst;\n    ngx_int_t         rc;\n    ngx_uint_t        i, start_line;\n    ngx_array_t      *saved;\n    enum {\n        parse_block = 0,\n        parse_param,\n    } type;\n\n    if (cf->conf_file->file.fd != NGX_INVALID_FILE) {\n\n        type = parse_block;\n\n    } else {\n        type = parse_param;\n    }\n\n    saved = cf->args;\n\n    cf->args = ngx_array_create(cf->temp_pool, 4, sizeof(ngx_str_t));\n    if (cf->args == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx.token_len = 0;\n    start_line = cf->conf_file->line;\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n    lmcf->directive_line = start_line;\n\n    dd(\"init start line: %d\", (int) start_line);\n\n    ctx.start_line = start_line;\n\n    for ( ;; ) {\n        rc = ngx_http_lua_conf_read_lua_token(cf, &ctx);\n\n        dd(\"parser start line: %d\", (int) start_line);\n\n        switch (rc) {\n\n        case NGX_ERROR:\n            goto done;\n\n        case FOUND_LEFT_CURLY:\n\n            ctx.start_line = cf->conf_file->line;\n\n            if (type == parse_param) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"block directives are not supported \"\n                                   \"in -g option\");\n                goto failed;\n            }\n\n            level++;\n            dd(\"seen block start: level=%d\", (int) level);\n            break;\n\n        case FOUND_RIGHT_CURLY:\n\n            level--;\n            dd(\"seen block done: level=%d\", (int) level);\n\n            if (type != parse_block || level < 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"unexpected \\\"}\\\": level %d, \"\n                                   \"starting at line %ui\", level,\n                                   start_line);\n                goto failed;\n            }\n\n            if (level == 0) {\n                ngx_http_lua_assert(cf->handler);\n\n                src = cf->args->elts;\n\n                for (len = 0, i = 0; i < cf->args->nelts; i++) {\n                    len += src[i].len;\n                }\n\n                dd(\"saved nelts: %d\", (int) saved->nelts);\n                dd(\"temp nelts: %d\", (int) cf->args->nelts);\n#if 0\n                ngx_http_lua_assert(saved->nelts == 1);\n#endif\n\n                dst = ngx_array_push(saved);\n                if (dst == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                dst->len = len;\n                dst->len--;  /* skip the trailing '}' block terminator */\n\n                p = ngx_palloc(cf->pool, len);\n                if (p == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                dst->data = p;\n\n                for (i = 0; i < cf->args->nelts; i++) {\n                    p = ngx_copy(p, src[i].data, src[i].len);\n                }\n\n                p[-1] = '\\0';  /* override the last '}' char to null */\n\n                cf->args = saved;\n\n                rv = (*cf->handler)(cf, cmd, cf->handler_conf);\n                if (rv == NGX_CONF_OK) {\n                    goto done;\n                }\n\n                if (rv == NGX_CONF_ERROR) {\n                    goto failed;\n                }\n\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv);\n\n                goto failed;\n            }\n\n            break;\n\n        case FOUND_LBRACKET_STR:\n        case FOUND_LBRACKET_CMT:\n        case FOUND_RIGHT_LBRACKET:\n        case FOUND_COMMENT_LINE:\n        case FOUND_DOUBLE_QUOTED:\n        case FOUND_SINGLE_QUOTED:\n            break;\n\n        default:\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"unknown return value from the lexer: %i\", rc);\n            goto failed;\n        }\n    }\n\nfailed:\n\n    rc = NGX_ERROR;\n\ndone:\n\n    lmcf->directive_line = 0;\n\n    if (rc == NGX_ERROR) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_conf_read_lua_token(ngx_conf_t *cf,\n    ngx_http_lua_block_parser_ctx_t *ctx)\n{\n    enum {\n        OVEC_SIZE = 2,\n    };\n    int          i, rc;\n    int          ovec[OVEC_SIZE];\n    u_char      *start, *p, *q, ch;\n    off_t        file_size;\n    size_t       len, buf_size;\n    ssize_t      n, size;\n    ngx_uint_t   start_line;\n    ngx_str_t   *word;\n    ngx_buf_t   *b;\n#if (nginx_version >= 1009002)\n    ngx_buf_t   *dump;\n#endif\n\n    b = cf->conf_file->buffer;\n#if (nginx_version >= 1009002)\n    dump = cf->conf_file->dump;\n#endif\n    start = b->pos;\n    start_line = cf->conf_file->line;\n    buf_size = b->end - b->start;\n\n    dd(\"lexer start line: %d\", (int) start_line);\n\n    file_size = ngx_file_size(&cf->conf_file->file.info);\n\n    for ( ;; ) {\n\n        if (b->pos >= b->last\n            || (b->last - b->pos < (b->end - b->start) / 2\n                && cf->conf_file->file.offset < file_size))\n        {\n\n            if (cf->conf_file->file.offset >= file_size) {\n\n                cf->conf_file->line = ctx->start_line;\n\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"unexpected end of file, expecting \"\n                                   \"terminating characters for lua code \"\n                                   \"block\");\n                return NGX_ERROR;\n            }\n\n            len = b->last - start;\n\n            if (len == buf_size) {\n\n                cf->conf_file->line = start_line;\n\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"too long lua code block, probably \"\n                                   \"missing terminating characters\");\n\n                return NGX_ERROR;\n            }\n\n            if (len) {\n                ngx_memmove(b->start, start, len);\n            }\n\n            size = (ssize_t) (file_size - cf->conf_file->file.offset);\n\n            if (size > b->end - (b->start + len)) {\n                size = b->end - (b->start + len);\n            }\n\n            n = ngx_read_file(&cf->conf_file->file, b->start + len, size,\n                              cf->conf_file->file.offset);\n\n            if (n == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (n != size) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   ngx_read_file_n \" returned \"\n                                   \"only %z bytes instead of %z\",\n                                   n, size);\n                return NGX_ERROR;\n            }\n\n            b->pos = b->start + (b->pos - start);\n            b->last = b->start + len + n;\n            start = b->start;\n\n#if (nginx_version >= 1009002)\n            if (dump) {\n                dump->last = ngx_cpymem(dump->last, b->start + len, size);\n            }\n#endif\n        }\n\n        rc = ngx_http_lua_lex(b->pos, b->last - b->pos, ovec);\n\n        if (rc < 0) {  /* no match */\n            /* alas. the lexer does not yet support streaming processing. need\n             * more work below */\n\n            if (cf->conf_file->file.offset >= file_size) {\n\n                cf->conf_file->line = ctx->start_line;\n\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"unexpected end of file, expecting \"\n                                   \"terminating characters for lua code \"\n                                   \"block\");\n                return NGX_ERROR;\n            }\n\n            len = b->last - b->pos;\n\n            if (len == buf_size) {\n\n                cf->conf_file->line = start_line;\n\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"too long lua code block, probably \"\n                                   \"missing terminating characters\");\n\n                return NGX_ERROR;\n            }\n\n            if (len) {\n                ngx_memmove(b->start, b->pos, len);\n            }\n\n            size = (ssize_t) (file_size - cf->conf_file->file.offset);\n\n            if (size > b->end - (b->start + len)) {\n                size = b->end - (b->start + len);\n            }\n\n            n = ngx_read_file(&cf->conf_file->file, b->start + len, size,\n                              cf->conf_file->file.offset);\n\n            if (n == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (n != size) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   ngx_read_file_n \" returned \"\n                                   \"only %z bytes instead of %z\",\n                                   n, size);\n                return NGX_ERROR;\n            }\n\n            b->pos = b->start + len;\n            b->last = b->pos + n;\n            start = b->start;\n\n            continue;\n        }\n\n        if (rc == FOUND_LEFT_LBRACKET_STR || rc == FOUND_LEFT_LBRACKET_CMT) {\n\n            /* we update the line numbers for best error messages when the\n             * closing long bracket is missing */\n\n            for (i = 0; i < ovec[0]; i++) {\n                ch = b->pos[i];\n                if (ch == LF) {\n                    cf->conf_file->line++;\n                }\n            }\n\n            b->pos += ovec[0];\n            ovec[1] -= ovec[0];\n            ovec[0] = 0;\n\n            if (rc == FOUND_LEFT_LBRACKET_CMT) {\n                p = &b->pos[2];     /* we skip the leading \"--\" prefix */\n                rc = FOUND_LBRACKET_CMT;\n\n            } else {\n                p = b->pos;\n                rc = FOUND_LBRACKET_STR;\n            }\n\n            /* we temporarily rewrite [=*[ in the input buffer to ]=*] to\n             * construct the pattern for the corresponding closing long\n             * bracket without additional buffers. */\n\n            ngx_http_lua_assert(p[0] == '[');\n            p[0] = ']';\n\n            ngx_http_lua_assert(b->pos[ovec[1] - 1] == '[');\n            b->pos[ovec[1] - 1] = ']';\n\n            /* search for the corresponding closing bracket */\n\n            dd(\"search pattern for the closing long bracket: \\\"%.*s\\\" (len=%d)\",\n               (int) (b->pos + ovec[1] - p), p, (int) (b->pos + ovec[1] - p));\n\n            q = ngx_http_lua_strlstrn(b->pos + ovec[1], b->last, p,\n                                      b->pos + ovec[1] - p - 1);\n\n            if (q == NULL) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"Lua code block missing the closing \"\n                                   \"long bracket \\\"%*s\\\", \"\n                                   \"the inlined Lua code may be too long\",\n                                   b->pos + ovec[1] - p, p);\n                return NGX_ERROR;\n            }\n\n            /* restore the original opening long bracket */\n\n            p[0] = '[';\n            b->pos[ovec[1] - 1] = '[';\n\n            ovec[1] = q - b->pos + b->pos + ovec[1] - p;\n\n            dd(\"found long bracket token: \\\"%.*s\\\"\",\n               (int) (ovec[1] - ovec[0]), b->pos + ovec[0]);\n        }\n\n        for (i = 0; i < ovec[1]; i++) {\n            ch = b->pos[i];\n            if (ch == LF) {\n                cf->conf_file->line++;\n            }\n        }\n\n        b->pos += ovec[1];\n        ctx->token_len = ovec[1] - ovec[0];\n\n        break;\n    }\n\n    word = ngx_array_push(cf->args);\n    if (word == NULL) {\n        return NGX_ERROR;\n    }\n\n    word->data = ngx_pnalloc(cf->temp_pool, b->pos - start);\n    if (word->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    len = b->pos - start;\n    ngx_memcpy(word->data, start, len);\n    word->len = len;\n\n    return rc;\n}\n\n\nchar *\nngx_http_lua_capture_error_log(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n#ifndef HAVE_INTERCEPT_ERROR_LOG_PATCH\n    return \"not found: missing the capture error log patch for nginx\";\n#else\n    ngx_str_t                     *value;\n    ssize_t                        size;\n    u_char                        *data;\n    ngx_cycle_t                   *cycle;\n    ngx_http_lua_main_conf_t      *lmcf = conf;\n    ngx_http_lua_log_ringbuf_t    *ringbuf;\n\n    value = cf->args->elts;\n    cycle = cf->cycle;\n\n    if (lmcf->requires_capture_log) {\n        return \"is duplicate\";\n    }\n\n    if (value[1].len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid capture error log size \\\"%V\\\"\",\n                           &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    size = ngx_parse_size(&value[1]);\n\n    if (size < NGX_MAX_ERROR_STR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid capture error log size \\\"%V\\\", \"\n                           \"minimum size is %d\", &value[1],\n                           NGX_MAX_ERROR_STR);\n        return NGX_CONF_ERROR;\n    }\n\n    if (cycle->intercept_error_log_handler) {\n        return \"capture error log handler has been hooked\";\n    }\n\n    ringbuf = (ngx_http_lua_log_ringbuf_t *)\n              ngx_palloc(cf->pool, sizeof(ngx_http_lua_log_ringbuf_t));\n    if (ringbuf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    data = ngx_palloc(cf->pool, size);\n    if (data == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_http_lua_log_ringbuf_init(ringbuf, data, size);\n\n    lmcf->requires_capture_log = 1;\n    cycle->intercept_error_log_handler = (ngx_log_intercept_pt)\n                                         ngx_http_lua_capture_log_handler;\n    cycle->intercept_error_log_data = ringbuf;\n\n    return NGX_CONF_OK;\n#endif\n}\n\n\n/*\n * ngx_http_lua_strlstrn() is intended to search for static substring\n * with known length in string until the argument last. The argument n\n * must be length of the second substring - 1.\n */\n\nstatic u_char *\nngx_http_lua_strlstrn(u_char *s1, u_char *last, u_char *s2, size_t n)\n{\n    ngx_uint_t  c1, c2;\n\n    c2 = (ngx_uint_t) *s2++;\n    last -= n;\n\n    do {\n        do {\n            if (s1 >= last) {\n                return NULL;\n            }\n\n            c1 = (ngx_uint_t) *s1++;\n\n            dd(\"testing char '%c' vs '%c'\", (int) c1, (int) c2);\n\n        } while (c1 != c2);\n\n        dd(\"testing against pattern \\\"%.*s\\\"\", (int) n, s2);\n\n    } while (ngx_strncmp(s1, s2, n) != 0);\n\n    return --s1;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_directive.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_DIRECTIVE_H_INCLUDED_\n#define _NGX_HTTP_LUA_DIRECTIVE_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nchar *ngx_http_lua_shared_dict(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_http_lua_package_cpath(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_package_path(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_regex_cache_max_entries(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_regex_match_limit(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_precontent_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_precontent_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_content_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_content_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_server_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_server_rewrite_by_lua_block(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nchar *ngx_http_lua_rewrite_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_access_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_access_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_log_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_log_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_header_filter_by_lua_block(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nchar *ngx_http_lua_header_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_body_filter_by_lua_block(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nchar *ngx_http_lua_body_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_init_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_init_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_init_worker_by_lua_block(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nchar *ngx_http_lua_init_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_exit_worker_by_lua_block(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nchar *ngx_http_lua_exit_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_http_lua_load_resty_core(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n#if defined(NDK) && NDK\n\nchar *ngx_http_lua_set_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_set_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_http_lua_set_by_lua_file(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nngx_int_t ngx_http_lua_filter_set_by_lua_inline(ngx_http_request_t *r,\n    ngx_str_t *val, ngx_http_variable_value_t *v, void *data);\nngx_int_t ngx_http_lua_filter_set_by_lua_file(ngx_http_request_t *r,\n    ngx_str_t *val, ngx_http_variable_value_t *v, void *data);\n\n#endif\n\nchar *ngx_http_lua_rewrite_no_postpone(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_lua_conf_lua_block_parse(ngx_conf_t *cf,\n    ngx_command_t *cmd);\nchar *ngx_http_lua_capture_error_log(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nu_char *ngx_http_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag,\n    size_t tag_len, size_t *chunkname_len);\n\n#endif /* _NGX_HTTP_LUA_DIRECTIVE_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_exception.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_exception.h\"\n#include \"ngx_http_lua_util.h\"\n\n\n/*  longjmp mark for restoring nginx execution after Lua VM crashing */\njmp_buf ngx_http_lua_exception;\n\n/**\n * Override default Lua panic handler, output VM crash reason to nginx error\n * log, and restore execution to the nearest jmp-mark.\n *\n * @param L Lua state pointer\n * @retval Long jump to the nearest jmp-mark, never returns.\n * @note nginx request pointer should be stored in Lua thread's globals table\n * in order to make logging working.\n * */\nint\nngx_http_lua_atpanic(lua_State *L)\n{\n#ifdef NGX_LUA_ABORT_AT_PANIC\n    abort();\n#else\n    u_char                  *s = NULL;\n    size_t                   len = 0;\n\n    if (lua_type(L, -1) == LUA_TSTRING) {\n        s = (u_char *) lua_tolstring(L, -1, &len);\n    }\n\n    if (s == NULL) {\n        s = (u_char *) \"unknown reason\";\n        len = sizeof(\"unknown reason\") - 1;\n    }\n\n    ngx_log_stderr(0, \"lua atpanic: Lua VM crashed, reason: %*s\", len, s);\n    ngx_quit = 1;\n\n    /*  restore nginx execution */\n    NGX_LUA_EXCEPTION_THROW(1);\n\n    /* impossible to reach here */\n#endif\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_exception.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_EXCEPTION_H_INCLUDED_\n#define _NGX_HTTP_LUA_EXCEPTION_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\n#define NGX_LUA_EXCEPTION_TRY                                                \\\n    if (setjmp(ngx_http_lua_exception) == 0)\n\n#define NGX_LUA_EXCEPTION_CATCH                                              \\\n    else\n\n#define NGX_LUA_EXCEPTION_THROW(x)                                           \\\n    longjmp(ngx_http_lua_exception, (x))\n\n\nextern jmp_buf ngx_http_lua_exception;\n\n\nint ngx_http_lua_atpanic(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_EXCEPTION_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_exitworkerby.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_exitworkerby.h\"\n#include \"ngx_http_lua_util.h\"\n\n#if (NGX_THREADS)\n#include \"ngx_http_lua_worker_thread.h\"\n#endif\n\n\nvoid\nngx_http_lua_exit_worker(ngx_cycle_t *cycle)\n{\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_connection_t            *c = NULL;\n    ngx_http_request_t          *r = NULL;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_conf_ctx_t         *conf_ctx;\n\n#if (NGX_THREADS)\n    ngx_http_lua_thread_exit_process();\n#endif\n\n    lmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_lua_module);\n    if (lmcf == NULL\n        || lmcf->exit_worker_handler == NULL\n        || lmcf->lua == NULL\n#if !(NGX_WIN32)\n        || (ngx_process == NGX_PROCESS_HELPER\n#   ifdef HAVE_PRIVILEGED_PROCESS_PATCH\n            && !ngx_is_privileged_agent\n#   endif\n           )\n#endif  /* NGX_WIN32 */\n       )\n    {\n        return;\n    }\n\n    conf_ctx = ((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index]);\n\n    c = ngx_http_lua_create_fake_connection(NULL);\n    if (c == NULL) {\n        goto failed;\n    }\n\n    c->log = ngx_cycle->log;\n\n    r = ngx_http_lua_create_fake_request(c);\n    if (r == NULL) {\n        goto failed;\n    }\n\n    r->main_conf = conf_ctx->main_conf;\n    r->srv_conf = conf_ctx->srv_conf;\n    r->loc_conf = conf_ctx->loc_conf;\n\n    ctx = ngx_http_lua_create_ctx(r);\n    if (ctx == NULL) {\n        goto failed;\n    }\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_EXIT_WORKER;\n    ctx->cur_co_ctx = NULL;\n\n    ngx_http_lua_set_req(lmcf->lua, r);\n\n    (void) lmcf->exit_worker_handler(cycle->log, lmcf, lmcf->lua);\n\n    ngx_destroy_pool(c->pool);\n    return;\n\nfailed:\n\n    if (c) {\n        ngx_http_lua_close_fake_connection(c);\n    }\n\n    return;\n}\n\n\nngx_int_t\nngx_http_lua_exit_worker_by_inline(ngx_log_t *log,\n    ngx_http_lua_main_conf_t *lmcf, lua_State *L)\n{\n    int         status;\n    const char *chunkname;\n\n    if (lmcf->exit_worker_chunkname == NULL) {\n        chunkname = \"=exit_worker_by_lua\";\n\n    } else {\n        chunkname = (const char *) lmcf->exit_worker_chunkname;\n    }\n\n    status = luaL_loadbuffer(L, (char *) lmcf->exit_worker_src.data,\n                             lmcf->exit_worker_src.len, chunkname)\n             || ngx_http_lua_do_call(log, L);\n\n    return ngx_http_lua_report(log, L, status, \"exit_worker_by_lua\");\n}\n\n\nngx_int_t\nngx_http_lua_exit_worker_by_file(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf,\n    lua_State *L)\n{\n    int         status;\n\n    status = luaL_loadfile(L, (char *) lmcf->exit_worker_src.data)\n             || ngx_http_lua_do_call(log, L);\n\n    return ngx_http_lua_report(log, L, status, \"exit_worker_by_lua_file\");\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_exitworkerby.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_EXITWORKERBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_EXITWORKERBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_exit_worker_by_inline(ngx_log_t *log,\n    ngx_http_lua_main_conf_t *lmcf, lua_State *L);\n\nngx_int_t ngx_http_lua_exit_worker_by_file(ngx_log_t *log,\n    ngx_http_lua_main_conf_t *lmcf, lua_State *L);\n\nvoid ngx_http_lua_exit_worker(ngx_cycle_t *cycle);\n\n\n#endif /* _NGX_HTTP_LUA_EXITWORKERBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_headerfilterby.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n#include \"ngx_http_lua_headerfilterby.h\"\n#include \"ngx_http_lua_exception.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_pcrefix.h\"\n#include \"ngx_http_lua_log.h\"\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_headers.h\"\n#include \"ngx_http_lua_string.h\"\n#include \"ngx_http_lua_misc.h\"\n#include \"ngx_http_lua_consts.h\"\n#include \"ngx_http_lua_shdict.h\"\n\n\nstatic ngx_http_output_header_filter_pt ngx_http_next_header_filter;\n\n\n/**\n * Set environment table for the given code closure.\n *\n * Before:\n *         | code closure | <- top\n *         |      ...     |\n *\n * After:\n *         | code closure | <- top\n *         |      ...     |\n * */\nstatic void\nngx_http_lua_header_filter_by_lua_env(lua_State *L, ngx_http_request_t *r)\n{\n    ngx_http_lua_set_req(L, r);\n\n#ifndef OPENRESTY_LUAJIT\n    /**\n     * we want to create empty environment for current script\n     *\n     * newt = {}\n     * newt[\"_G\"] = newt\n     * setmetatable(newt, {__index = _G})\n     *\n     * if a function or symbol is not defined in our env, __index will lookup\n     * in the global env.\n     *\n     * all variables created in the script-env will be thrown away at the end\n     * of the script run.\n     * */\n    ngx_http_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);\n\n    /*  {{{ make new env inheriting main thread's globals table */\n    lua_createtable(L, 0, 1 /* nrec */);   /* the metatable for the new env */\n    ngx_http_lua_get_globals_table(L);\n    lua_setfield(L, -2, \"__index\");\n    lua_setmetatable(L, -2);    /*  setmetatable({}, {__index = _G}) */\n    /*  }}} */\n\n    lua_setfenv(L, -2);    /*  set new running env for the code closure */\n#endif /* OPENRESTY_LUAJIT */\n}\n\n\nngx_int_t\nngx_http_lua_header_filter_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    int              old_exit_code = 0;\n    ngx_int_t        rc;\n    u_char          *err_msg;\n    size_t           len;\n#if (NGX_PCRE)\n    ngx_pool_t      *old_pool;\n#endif\n    ngx_http_lua_ctx_t          *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx->exited) {\n        old_exit_code = ctx->exit_code;\n    }\n\n    /*  initialize nginx context in Lua VM, code chunk at stack top    sp = 1 */\n    ngx_http_lua_header_filter_by_lua_env(L, r);\n\n#if (NGX_PCRE)\n    /* XXX: work-around to nginx regex subsystem */\n    old_pool = ngx_http_lua_pcre_malloc_init(r->pool);\n#endif\n\n    lua_pushcfunction(L, ngx_http_lua_traceback);\n    lua_insert(L, 1);  /* put it under chunk and args */\n\n    /*  protected call user code */\n    rc = lua_pcall(L, 0, 1, 1);\n\n    lua_remove(L, 1);  /* remove traceback function */\n\n#if (NGX_PCRE)\n    /* XXX: work-around to nginx regex subsystem */\n    ngx_http_lua_pcre_malloc_done(old_pool);\n#endif\n\n    dd(\"rc == %d\", (int) rc);\n\n    if (rc != 0) {\n        /*  error occurred when running loaded code */\n        err_msg = (u_char *) lua_tolstring(L, -1, &len);\n\n        if (err_msg == NULL) {\n            err_msg = (u_char *) \"unknown reason\";\n            len = sizeof(\"unknown reason\") - 1;\n        }\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"failed to run header_filter_by_lua*: %*s\", len, err_msg);\n\n        lua_settop(L, 0); /*  clear remaining elems on stack */\n\n        return NGX_ERROR;\n    }\n\n    dd(\"exited: %d, exit code: %d, old exit code: %d\",\n       (int) ctx->exited, (int) ctx->exit_code, (int) old_exit_code);\n\n#if 1\n    /*  clear Lua stack */\n    lua_settop(L, 0);\n#endif\n\n    if (ctx->exited && ctx->exit_code != old_exit_code) {\n        if (ctx->exit_code == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        dd(\"finalize request with %d\", (int) ctx->exit_code);\n\n        rc = ngx_http_filter_finalize_request(r, &ngx_http_lua_module,\n                                              ctx->exit_code);\n        if (rc == NGX_ERROR || rc == NGX_AGAIN) {\n            return rc;\n        }\n\n        return NGX_DECLINED;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_lua_header_filter_inline(ngx_http_request_t *r)\n{\n    lua_State                   *L;\n    ngx_int_t                    rc;\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->header_filter_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua inline script (w/ cache) sp = 1 */\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       llcf->header_filter_src.value.data,\n                                       llcf->header_filter_src.value.len,\n                                       &llcf->header_filter_src_ref,\n                                       llcf->header_filter_src_key,\n                                       (const char *)\n                                       llcf->header_filter_chunkname);\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    dd(\"calling header filter by chunk\");\n\n    return ngx_http_lua_header_filter_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_header_filter_file(ngx_http_request_t *r)\n{\n    lua_State                       *L;\n    ngx_int_t                        rc;\n    u_char                          *script_path;\n    ngx_http_lua_loc_conf_t         *llcf;\n    ngx_str_t                        eval_src;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    /* Eval nginx variables in code path string first */\n    if (ngx_http_complex_value(r, &llcf->header_filter_src, &eval_src)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,\n                                           eval_src.len);\n\n    if (script_path == NULL) {\n        return NGX_ERROR;\n    }\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->header_filter_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua script file (w/ cache)        sp = 1 */\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,\n                                     &llcf->header_filter_src_ref,\n                                     llcf->header_filter_src_key);\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_header_filter_by_chunk(L, r);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_header_filter(ngx_http_request_t *r)\n{\n    ngx_http_lua_loc_conf_t     *llcf;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_int_t                    rc;\n    ngx_pool_cleanup_t          *cln;\n    uint16_t                     old_context;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua header filter for user lua code, uri \\\"%V\\\"\", &r->uri);\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->body_filter_handler) {\n        r->filter_need_in_memory = 1;\n    }\n\n    if (llcf->header_filter_handler == NULL) {\n        dd(\"no header filter handler found\");\n        return ngx_http_next_header_filter(r);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    dd(\"ctx = %p\", ctx);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (ctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n        if (cln == NULL) {\n            return NGX_ERROR;\n        }\n\n        cln->handler = ngx_http_lua_request_cleanup_handler;\n        cln->data = ctx;\n        ctx->cleanup = &cln->handler;\n    }\n\n    old_context = ctx->context;\n    ctx->context = NGX_HTTP_LUA_CONTEXT_HEADER_FILTER;\n\n    dd(\"calling header filter handler\");\n    rc = llcf->header_filter_handler(r);\n\n    ctx->context = old_context;\n\n    if (rc == NGX_DECLINED) {\n        return NGX_OK;\n    }\n\n    if (rc == NGX_AGAIN || rc == NGX_ERROR) {\n        return rc;\n    }\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nngx_int_t\nngx_http_lua_header_filter_init(void)\n{\n    dd(\"calling header filter init\");\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_lua_header_filter;\n\n    return NGX_OK;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_headerfilterby.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_HEADERFILTERBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_HEADERFILTERBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nextern ngx_http_output_header_filter_pt ngx_http_lua_next_filter_header_filter;\n\n\nngx_int_t ngx_http_lua_header_filter_init(void);\n\nngx_int_t ngx_http_lua_header_filter_by_chunk(lua_State *L,\n        ngx_http_request_t *r);\n\nngx_int_t ngx_http_lua_header_filter_inline(ngx_http_request_t *r);\n\nngx_int_t ngx_http_lua_header_filter_file(ngx_http_request_t *r);\n\n\n#endif /* _NGX_HTTP_LUA_HEADERFILTERBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_headers.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_headers.h\"\n#include \"ngx_http_lua_headers_out.h\"\n#include \"ngx_http_lua_headers_in.h\"\n#include \"ngx_http_lua_util.h\"\n\n\nstatic int ngx_http_lua_ngx_req_http_version(lua_State *L);\nstatic int ngx_http_lua_ngx_req_raw_header(lua_State *L);\nstatic int ngx_http_lua_ngx_req_header_set_helper(lua_State *L);\nstatic int ngx_http_lua_ngx_resp_get_headers(lua_State *L);\nstatic int ngx_http_lua_ngx_req_header_set(lua_State *L);\n#if (nginx_version >= 1011011)\nvoid ngx_http_lua_ngx_raw_header_cleanup(void *data);\n#endif\n\n\nvoid\nngx_http_lua_inject_resp_header_api(lua_State *L)\n{\n    lua_createtable(L, 0, 1); /* .resp */\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_resp_get_headers);\n    lua_setfield(L, -2, \"get_headers\");\n\n    lua_setfield(L, -2, \"resp\");\n}\n\n\nvoid\nngx_http_lua_inject_req_header_api(lua_State *L)\n{\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_http_version);\n    lua_setfield(L, -2, \"http_version\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_raw_header);\n    lua_setfield(L, -2, \"raw_header\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_header_set);\n    lua_setfield(L, -2, \"set_header\");\n}\n\n\nstatic int\nngx_http_lua_ngx_req_http_version(lua_State *L)\n{\n    ngx_http_request_t          *r;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request object found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    switch (r->http_version) {\n    case NGX_HTTP_VERSION_9:\n        lua_pushnumber(L, 0.9);\n        break;\n\n    case NGX_HTTP_VERSION_10:\n        lua_pushnumber(L, 1.0);\n        break;\n\n    case NGX_HTTP_VERSION_11:\n        lua_pushnumber(L, 1.1);\n        break;\n\n#ifdef NGX_HTTP_VERSION_20\n    case NGX_HTTP_VERSION_20:\n        lua_pushnumber(L, 2.0);\n        break;\n#endif\n\n#ifdef NGX_HTTP_VERSION_30\n    case NGX_HTTP_VERSION_30:\n        lua_pushnumber(L, 3.0);\n        break;\n#endif\n\n    default:\n        lua_pushnil(L);\n        break;\n    }\n\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_ngx_req_raw_header(lua_State *L)\n{\n    int                          n, line_break_len;\n    u_char                      *data, *p, *last, *pos;\n    unsigned                     no_req_line = 0, found;\n    size_t                       size;\n    ngx_buf_t                   *b, *first = NULL;\n    ngx_int_t                    i, j;\n#if (nginx_version >= 1011011)\n    ngx_buf_t                  **bb;\n    ngx_chain_t                 *cl;\n    ngx_http_lua_main_conf_t    *lmcf;\n#endif\n    ngx_connection_t            *c;\n    ngx_http_request_t          *r, *mr;\n    ngx_http_connection_t       *hc;\n\n    n = lua_gettop(L);\n    if (n > 0) {\n        no_req_line = lua_toboolean(L, 1);\n    }\n\n    dd(\"no req line: %d\", (int) no_req_line);\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request object found\");\n    }\n\n#if (nginx_version >= 1011011)\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n#endif\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    mr = r->main;\n    hc = mr->http_connection;\n    c = mr->connection;\n\n#if (NGX_HTTP_V2)\n    if (mr->stream) {\n        return luaL_error(L, \"http2 requests not supported yet\");\n    }\n#endif\n\n#if 1\n    dd(\"hc->nbusy: %d\", (int) hc->nbusy);\n\n    if (hc->nbusy) {\n#if (nginx_version >= 1011011)\n        dd(\"hc->busy: %p %p %p %p\", hc->busy->buf->start, hc->busy->buf->pos,\n           hc->busy->buf->last, hc->busy->buf->end);\n#else\n        dd(\"hc->busy: %p %p %p %p\", hc->busy[0]->start, hc->busy[0]->pos,\n           hc->busy[0]->last, hc->busy[0]->end);\n#endif\n    }\n\n    dd(\"request line: %p %p\", mr->request_line.data,\n       mr->request_line.data + mr->request_line.len);\n\n    dd(\"header in: %p %p %p %p\", mr->header_in->start,\n       mr->header_in->pos, mr->header_in->last,\n       mr->header_in->end);\n\n    dd(\"c->buffer: %p %p %p %p\", c->buffer->start,\n       c->buffer->pos, c->buffer->last,\n       c->buffer->end);\n#endif\n\n    size = 0;\n    b = c->buffer;\n\n    if (mr->request_line.len == 0) {\n        /* return empty string on invalid request */\n        lua_pushlstring(L, \"\", 0);\n        return 1;\n    }\n\n    if (mr->request_line.data[mr->request_line.len] == CR) {\n        line_break_len = 2;\n\n    } else {\n        line_break_len = 1;\n    }\n\n    if (mr->request_line.data >= b->start\n        && mr->request_line.data + mr->request_line.len\n           + line_break_len <= b->pos)\n    {\n        first = b;\n        size += b->pos - mr->request_line.data;\n    }\n\n    dd(\"size: %d\", (int) size);\n\n    if (hc->nbusy) {\n#if (nginx_version >= 1011011)\n        if (hc->nbusy > lmcf->busy_buf_ptr_count) {\n            if (lmcf->busy_buf_ptrs) {\n                ngx_free(lmcf->busy_buf_ptrs);\n            }\n\n            lmcf->busy_buf_ptrs = ngx_alloc(hc->nbusy * sizeof(ngx_buf_t *),\n                                            r->connection->log);\n\n            if (lmcf->busy_buf_ptrs == NULL) {\n                return luaL_error(L, \"no memory\");\n            }\n\n            lmcf->busy_buf_ptr_count = hc->nbusy;\n        }\n\n        bb = lmcf->busy_buf_ptrs;\n        for (cl = hc->busy; cl; cl = cl->next) {\n            *bb++ = cl->buf;\n        }\n#endif\n        b = NULL;\n\n#if (nginx_version >= 1011011)\n        bb = lmcf->busy_buf_ptrs;\n        for (i = hc->nbusy; i > 0; i--) {\n            b = bb[i - 1];\n#else\n        for (i = 0; i < hc->nbusy; i++) {\n            b = hc->busy[i];\n#endif\n\n            dd(\"busy buf: %d: [%.*s]\", (int) i, (int) (b->pos - b->start),\n               b->start);\n\n            if (first == NULL) {\n                if (mr->request_line.data >= b->pos\n                    || mr->request_line.data\n                       + mr->request_line.len + line_break_len\n                       <= b->start)\n                {\n                    continue;\n                }\n\n                dd(\"found first at %d\", (int) i);\n                first = b;\n            }\n\n            dd(\"adding size %d\", (int) (b->pos - b->start));\n            size += b->pos - b->start;\n        }\n    }\n\n    size++;  /* plus the null terminator, as required by the later\n                ngx_strstr() call */\n\n    dd(\"header size: %d\", (int) size);\n\n    data = lua_newuserdata(L, size);\n    last = data;\n\n    b = c->buffer;\n    found = 0;\n\n    if (first == b) {\n        found = 1;\n        pos = b->pos;\n\n        if (no_req_line) {\n            last = ngx_copy(data,\n                            mr->request_line.data\n                            + mr->request_line.len + line_break_len,\n                            pos - mr->request_line.data\n                            - mr->request_line.len - line_break_len);\n\n        } else {\n            last = ngx_copy(data, mr->request_line.data,\n                            pos - mr->request_line.data);\n        }\n\n        if (b != mr->header_in) {\n            /* skip truncated header entries (if any) */\n            while (last > data && last[-1] != LF && last[-1] != '\\0') {\n                last--;\n            }\n        }\n\n        i = 0;\n        for (p = data; p != last; p++) {\n            if (*p == '\\0') {\n                i++;\n                if (p + 1 != last && *(p + 1) == LF) {\n                    *p = CR;\n\n                } else if (i % 2 == 1) {\n                    *p = ':';\n\n                } else {\n                    *p = LF;\n                }\n            }\n        }\n    }\n\n    if (hc->nbusy) {\n\n#if (nginx_version >= 1011011)\n        bb = lmcf->busy_buf_ptrs;\n        for (i = hc->nbusy - 1; i >= 0; i--) {\n            b = bb[i];\n#else\n        for (i = 0; i < hc->nbusy; i++) {\n            b = hc->busy[i];\n#endif\n\n            if (!found) {\n                if (b != first) {\n                    continue;\n                }\n\n                dd(\"found first\");\n                found = 1;\n            }\n\n            p = last;\n\n            pos = b->pos;\n\n            if (b == first) {\n                dd(\"request line: %.*s\", (int) mr->request_line.len,\n                   mr->request_line.data);\n\n                if (no_req_line) {\n                    last = ngx_copy(last,\n                                    mr->request_line.data\n                                    + mr->request_line.len + line_break_len,\n                                    pos - mr->request_line.data\n                                    - mr->request_line.len - line_break_len);\n\n                } else {\n                    last = ngx_copy(last,\n                                    mr->request_line.data,\n                                    pos - mr->request_line.data);\n\n                }\n\n            } else {\n                last = ngx_copy(last, b->start, pos - b->start);\n            }\n\n#if 1\n            /* skip truncated header entries (if any) */\n            while (last > p && last[-1] != LF && last[-1] != '\\0') {\n                last--;\n            }\n#endif\n\n            j = 0;\n            for (; p != last; p++) {\n                if (*p == '\\0') {\n                    j++;\n                    if (p + 1 == last) {\n                        *p = LF;\n\n                    } else if (*(p + 1) == LF) {\n                        *p = CR;\n\n                    } else if (j % 2 == 1) {\n                        *p = ':';\n\n                    } else {\n                        *p = LF;\n                    }\n                }\n            }\n\n            if (b == mr->header_in) {\n                break;\n            }\n        }\n    }\n\n    *last++ = '\\0';\n\n    if (last - data > (ssize_t) size) {\n        return luaL_error(L, \"buffer error: %d\", (int) (last - data - size));\n    }\n\n    /* strip the leading part (if any) of the request body in our header.\n     * the first part of the request body could slip in because nginx core's\n     * ngx_http_request_body_length_filter and etc can move r->header_in->pos\n     * in case that some of the body data has been preread into r->header_in.\n     */\n\n    if ((p = (u_char *) ngx_strstr(data, CRLF CRLF)) != NULL) {\n        last = p + sizeof(CRLF CRLF) - 1;\n\n    } else if ((p = (u_char *) ngx_strstr(data, CRLF \"\\n\")) != NULL) {\n        last = p + sizeof(CRLF \"\\n\") - 1;\n\n    } else if ((p = (u_char *) ngx_strstr(data, \"\\n\" CRLF)) != NULL) {\n        last = p + sizeof(\"\\n\" CRLF) - 1;\n\n    } else {\n        for (p = last - 1; p - data >= 2; p--) {\n            if (p[0] == LF && p[-1] == CR) {\n                p[-1] = LF;\n                last = p + 1;\n                break;\n            }\n\n            if (p[0] == LF && p[-1] == LF) {\n                last = p + 1;\n                break;\n            }\n        }\n    }\n\n    lua_pushlstring(L, (char *) data, last - data);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_ngx_resp_get_headers(lua_State *L)\n{\n    ngx_list_part_t    *part;\n    ngx_table_elt_t    *header;\n    ngx_http_request_t *r;\n    ngx_http_lua_ctx_t *ctx;\n    u_char             *lowcase_key = NULL;\n    size_t              lowcase_key_sz = 0;\n    ngx_uint_t          i;\n    int                 n;\n    int                 max;\n    int                 raw = 0;\n    int                 count = 0;\n    int                 truncated = 0;\n    int                 extra = 0;\n    u_char             *p = NULL;\n    size_t              len = 0;\n\n    n = lua_gettop(L);\n\n    if (n >= 1) {\n        if (lua_isnil(L, 1)) {\n            max = NGX_HTTP_LUA_MAX_HEADERS;\n\n        } else {\n            max = luaL_checkinteger(L, 1);\n        }\n\n        if (n >= 2) {\n            raw = lua_toboolean(L, 2);\n        }\n\n    } else {\n        max = NGX_HTTP_LUA_MAX_HEADERS;\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request object found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    part = &r->headers_out.headers.part;\n    count = part->nelts;\n    while (part->next != NULL) {\n        part = part->next;\n        count += part->nelts;\n    }\n\n    lua_createtable(L, 0, count + 2);\n\n    if (!raw) {\n        lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                              headers_metatable_key));\n        lua_rawget(L, LUA_REGISTRYINDEX);\n        lua_setmetatable(L, -2);\n    }\n\n#if 1\n    if (r->headers_out.content_type.len) {\n        extra++;\n        lua_pushliteral(L, \"content-type\");\n        lua_pushlstring(L, (char *) r->headers_out.content_type.data,\n                        r->headers_out.content_type.len);\n        lua_rawset(L, -3);\n    }\n\n    if (r->headers_out.content_length == NULL\n        && r->headers_out.content_length_n >= 0)\n    {\n        extra++;\n        lua_pushliteral(L, \"content-length\");\n        if (r->headers_out.content_length_n > NGX_MAX_INT32_VALUE) {\n            p = ngx_palloc(r->pool, NGX_OFF_T_LEN);\n            if (p == NULL) {\n                return luaL_error(L, \"no memory\");\n            }\n\n            len = ngx_snprintf(p, NGX_OFF_T_LEN, \"%O\",\n                               r->headers_out.content_length_n) - p;\n\n            lua_pushlstring(L, (char *) p, len);\n\n        } else {\n            lua_pushfstring(L, \"%d\", (int) r->headers_out.content_length_n);\n        }\n\n        lua_rawset(L, -3);\n    }\n\n    extra++;\n    lua_pushliteral(L, \"connection\");\n    if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {\n        lua_pushliteral(L, \"upgrade\");\n\n    } else if (r->keepalive) {\n        lua_pushliteral(L, \"keep-alive\");\n\n    } else {\n        lua_pushliteral(L, \"close\");\n    }\n\n    lua_rawset(L, -3);\n\n    if (r->chunked) {\n        extra++;\n        lua_pushliteral(L, \"transfer-encoding\");\n        lua_pushliteral(L, \"chunked\");\n        lua_rawset(L, -3);\n    }\n#endif\n\n    if (max > 0 && count + extra > max) {\n        truncated = 1;\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua exceeding response header limit %d > %d\",\n                       count + extra, max);\n        count = max - extra;\n    }\n\n    part = &r->headers_out.headers.part;\n    header = part->elts;\n\n    for (i = 0; /* void */; i++) {\n\n        dd(\"stack top: %d\", lua_gettop(L));\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n\n            part = part->next;\n            header = part->elts;\n            i = 0;\n        }\n\n        if (header[i].hash == 0) {\n            continue;\n        }\n\n        if (raw) {\n            lua_pushlstring(L, (char *) header[i].key.data, header[i].key.len);\n\n        } else {\n            /* nginx does not even bother initializing output header entry's\n             * \"lowcase_key\" field. so we cannot count on that at all. */\n            if (header[i].key.len > lowcase_key_sz) {\n                lowcase_key_sz = header[i].key.len * 2;\n\n                /* we allocate via Lua's GC to prevent in-request\n                 * leaks in the nginx request memory pools */\n                lowcase_key = lua_newuserdata(L, lowcase_key_sz);\n                lua_insert(L, 1);\n            }\n\n            ngx_strlow(lowcase_key, header[i].key.data, header[i].key.len);\n            lua_pushlstring(L, (char *) lowcase_key, header[i].key.len);\n        }\n\n        /* stack: [udata] table key */\n\n        lua_pushlstring(L, (char *) header[i].value.data,\n                        header[i].value.len); /* stack: [udata] table key\n                                                 value */\n\n        ngx_http_lua_set_multi_value_table(L, -3);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua response header: \\\"%V: %V\\\"\",\n                       &header[i].key, &header[i].value);\n\n        if (--count <= 0) {\n            break;\n        }\n    }  /* for */\n\n    if (truncated) {\n        lua_pushliteral(L, \"truncated\");\n        return 2;\n    }\n\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_ngx_req_header_set(lua_State *L)\n{\n    if (lua_gettop(L) != 2) {\n        return luaL_error(L, \"expecting two arguments, but seen %d\",\n                          lua_gettop(L));\n    }\n\n    return ngx_http_lua_ngx_req_header_set_helper(L);\n}\n\n\nstatic int\nngx_http_lua_ngx_req_header_set_helper(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    u_char                      *p;\n    ngx_str_t                    key;\n    ngx_str_t                    value;\n    ngx_uint_t                   i;\n    size_t                       len;\n    ngx_int_t                    rc;\n    ngx_uint_t                   n;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request object found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    if (r->http_version < NGX_HTTP_VERSION_10) {\n        return 0;\n    }\n\n    p = (u_char *) luaL_checklstring(L, 1, &len);\n\n    dd(\"key: %.*s, len %d\", (int) len, p, (int) len);\n\n#if 0\n    /* replace \"_\" with \"-\" */\n    for (i = 0; i < len; i++) {\n        if (p[i] == '_') {\n            p[i] = '-';\n        }\n    }\n#endif\n\n    key.data = ngx_palloc(r->pool, len + 1);\n    if (key.data == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    ngx_memcpy(key.data, p, len);\n\n    key.data[len] = '\\0';\n\n    key.len = len;\n\n    if (lua_type(L, 2) == LUA_TNIL) {\n        ngx_str_null(&value);\n\n    } else if (lua_type(L, 2) == LUA_TTABLE) {\n        n = lua_objlen(L, 2);\n        if (n == 0) {\n            ngx_str_null(&value);\n\n        } else {\n            for (i = 1; i <= n; i++) {\n                dd(\"header value table index %d, top: %d\", (int) i,\n                   lua_gettop(L));\n\n                lua_rawgeti(L, 2, i);\n                p = (u_char *) luaL_checklstring(L, -1, &len);\n\n                /*\n                 * we also copy the trailing '\\0' char here because nginx\n                 * header values must be null-terminated\n                 * */\n\n                value.data = ngx_palloc(r->pool, len + 1);\n                if (value.data == NULL) {\n                    return luaL_error(L, \"no memory\");\n                }\n\n                ngx_memcpy(value.data, p, len + 1);\n                value.len = len;\n\n                rc = ngx_http_lua_set_input_header(r, key, value,\n                                                   i == 1 /* override */);\n\n                if (rc == NGX_ERROR) {\n                    return luaL_error(L,\n                                      \"failed to set header %s (error: %d)\",\n                                      key.data, (int) rc);\n                }\n            }\n\n            return 0;\n        }\n\n    } else {\n\n        /*\n         * we also copy the trailing '\\0' char here because nginx\n         * header values must be null-terminated\n         * */\n\n        p = (u_char *) luaL_checklstring(L, 2, &len);\n        value.data = ngx_palloc(r->pool, len + 1);\n        if (value.data == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        ngx_memcpy(value.data, p, len + 1);\n        value.len = len;\n    }\n\n    dd(\"key: %.*s, value: %.*s\",\n       (int) key.len, key.data, (int) value.len, value.data);\n\n    rc = ngx_http_lua_set_input_header(r, key, value, 1 /* override */);\n\n    if (rc == NGX_ERROR) {\n        return luaL_error(L, \"failed to set header %s (error: %d)\",\n                          key.data, (int) rc);\n    }\n\n    return 0;\n}\n\n\nvoid\nngx_http_lua_create_headers_metatable(ngx_log_t *log, lua_State *L)\n{\n    int rc;\n    const char buf[] =\n        \"local tb, key = ...\\n\"\n        \"local new_key = string.gsub(string.lower(key), '_', '-')\\n\"\n        \"if new_key ~= key then return tb[new_key] else return nil end\";\n\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          headers_metatable_key));\n\n    /* metatable for ngx.req.get_headers(_, true) and\n     * ngx.resp.get_headers(_, true) */\n    lua_createtable(L, 0, 1);\n\n    rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, \"=headers metatable\");\n    if (rc != 0) {\n        ngx_log_error(NGX_LOG_ERR, log, 0,\n                      \"failed to load Lua code for the metamethod for \"\n                      \"headers: %i: %s\", rc, lua_tostring(L, -1));\n\n        lua_pop(L, 3);\n        return;\n    }\n\n    lua_setfield(L, -2, \"__index\");\n    lua_rawset(L, LUA_REGISTRYINDEX);\n}\n\n\nint\nngx_http_lua_ffi_req_get_headers_count(ngx_http_request_t *r, int max,\n    int *truncated)\n{\n    int                           count;\n    ngx_list_part_t              *part;\n#if (NGX_HTTP_V3)\n    int                           has_host = 0;\n    ngx_uint_t                    i;\n    ngx_table_elt_t              *header;\n#endif\n\n    if (r->connection->fd == (ngx_socket_t) -1) {\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    *truncated = 0;\n\n    if (max < 0) {\n        max = NGX_HTTP_LUA_MAX_HEADERS;\n    }\n\n    part = &r->headers_in.headers.part;\n\n#if (NGX_HTTP_V3)\n    count = 0;\n    header = part->elts;\n\n    if (r->http_version == NGX_HTTP_VERSION_30\n        && r->headers_in.server.data != NULL)\n    {\n        has_host = 1;\n        count++;\n    }\n\n    if (has_host == 1) {\n        for (i = 0; /* void */; i++) {\n            if (i >= part->nelts) {\n                if (part->next == NULL) {\n                    break;\n                }\n\n                part = part->next;\n                header = part->elts;\n                i = 0;\n            }\n\n            if (header[i].key.len == 4\n                && ngx_strncasecmp(header[i].key.data,\n                                   (u_char *) \"host\", 4) == 0)\n            {\n                continue;\n            }\n\n            count++;\n        }\n\n    } else {\n        count = part->nelts;\n        while (part->next != NULL) {\n            part = part->next;\n            count += part->nelts;\n        }\n    }\n#else\n    count = part->nelts;\n    while (part->next != NULL) {\n        part = part->next;\n        count += part->nelts;\n    }\n#endif\n\n    if (max > 0 && count > max) {\n        *truncated = 1;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua exceeding request header limit %d > %d\", count,\n                       max);\n        count = max;\n    }\n\n    return count;\n}\n\n\nint\nngx_http_lua_ffi_req_get_headers(ngx_http_request_t *r,\n    ngx_http_lua_ffi_table_elt_t *out, int count, int raw)\n{\n    int                           n;\n    ngx_uint_t                    i;\n    ngx_list_part_t              *part;\n    ngx_table_elt_t              *header;\n#if (NGX_HTTP_V3)\n    int                           has_host = 0;\n#endif\n\n    if (count <= 0) {\n        return NGX_OK;\n    }\n\n    n = 0;\n\n#if (NGX_HTTP_V3)\n    if (r->http_version == NGX_HTTP_VERSION_30\n        && r->headers_in.server.data != NULL)\n    {\n        out[n].key.data = (u_char *) \"host\";\n        out[n].key.len = sizeof(\"host\") - 1;\n        out[n].value.len = r->headers_in.server.len;\n        out[n].value.data = r->headers_in.server.data;\n        has_host = 1;\n        ++n;\n    }\n#endif\n\n    part = &r->headers_in.headers.part;\n    header = part->elts;\n\n    for (i = 0; /* void */; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n\n            part = part->next;\n            header = part->elts;\n            i = 0;\n        }\n\n#if (NGX_HTTP_V3)\n        if (has_host == 1 && header[i].key.len == 4\n            && ngx_strncasecmp(header[i].key.data, (u_char *) \"host\", 4) == 0)\n        {\n            continue;\n        }\n#endif\n\n        if (raw) {\n            out[n].key.data = header[i].key.data;\n            out[n].key.len = (int) header[i].key.len;\n\n        } else {\n            out[n].key.data = header[i].lowcase_key;\n            out[n].key.len = (int) header[i].key.len;\n        }\n\n        out[n].value.data = header[i].value.data;\n        out[n].value.len = (int) header[i].value.len;\n\n        if (++n == count) {\n            return NGX_OK;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_set_resp_header(ngx_http_request_t *r, const u_char *key_data,\n    size_t key_len, int is_nil, const u_char *sval, size_t sval_len,\n    ngx_http_lua_ffi_str_t *mvals, size_t mvals_len, int override,\n    char **errmsg)\n{\n    u_char                      *p;\n    ngx_str_t                    value, key;\n    ngx_uint_t                   i;\n    size_t                       len;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_int_t                    rc;\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_HTTP_LUA_FFI_NO_REQ_CTX;\n    }\n\n    if (r->connection->fd == (ngx_socket_t) -1) {\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    if (r->header_sent || ctx->header_sent) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \"attempt to \"\n                      \"set ngx.header.HEADER after sending out \"\n                      \"response headers\");\n        return NGX_DECLINED;\n    }\n\n    key.data = ngx_palloc(r->pool, key_len + 1);\n    if (key.data == NULL) {\n        goto nomem;\n    }\n\n    ngx_memcpy(key.data, key_data, key_len);\n    key.data[key_len] = '\\0';\n    key.len = key_len;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->transform_underscores_in_resp_headers) {\n        /* replace \"_\" with \"-\" */\n        p = key.data;\n        for (i = 0; i < key_len; i++) {\n            if (p[i] == '_') {\n                p[i] = '-';\n            }\n        }\n    }\n\n    ctx->headers_set = 1;\n\n    if (is_nil) {\n        value.data = NULL;\n        value.len = 0;\n\n    } else if (mvals) {\n\n        if (mvals_len == 0) {\n            value.data = NULL;\n            value.len = 0;\n\n        } else {\n            for (i = 0; i < mvals_len; i++) {\n                dd(\"header value table index %d\", (int) i);\n\n                p = mvals[i].data;\n                len = mvals[i].len;\n\n                value.data = ngx_palloc(r->pool, len);\n                if (value.data == NULL) {\n                    goto nomem;\n                }\n\n                ngx_memcpy(value.data, p, len);\n                value.len = len;\n\n                rc = ngx_http_lua_set_output_header(r, ctx, key, value,\n                                                    override && i == 0);\n\n                if (rc == NGX_ERROR) {\n                    *errmsg = \"failed to set header\";\n                    return NGX_ERROR;\n                }\n            }\n\n            return NGX_OK;\n        }\n\n    } else {\n        p = (u_char *) sval;\n        value.data = ngx_palloc(r->pool, sval_len);\n        if (value.data == NULL) {\n            goto nomem;\n        }\n\n        ngx_memcpy(value.data, p, sval_len);\n        value.len = sval_len;\n    }\n\n    dd(\"key: %.*s, value: %.*s\",\n       (int) key.len, key.data, (int) value.len, value.data);\n\n    rc = ngx_http_lua_set_output_header(r, ctx, key, value, override);\n\n    if (rc == NGX_ERROR) {\n        *errmsg = \"failed to set header\";\n        return NGX_ERROR;\n    }\n\n    return 0;\n\nnomem:\n\n    *errmsg = \"no memory\";\n    return NGX_ERROR;\n}\n\n\nint\nngx_http_lua_ffi_req_set_header(ngx_http_request_t *r, const u_char *key,\n    size_t key_len, const u_char *value, size_t value_len,\n    ngx_http_lua_ffi_str_t *mvals, size_t mvals_len, int override,\n    char **errmsg)\n{\n    u_char                      *p;\n    size_t                       len;\n    ngx_uint_t                   i;\n    ngx_str_t                    k, v;\n\n    if (r->connection->fd == (ngx_socket_t) -1) {  /* fake request */\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    if (r->http_version < NGX_HTTP_VERSION_10) {\n        return NGX_DECLINED;\n    }\n\n    k.data = ngx_palloc(r->pool, key_len + 1);\n    if (k.data == NULL) {\n        goto nomem;\n    }\n\n    ngx_memcpy(k.data, key, key_len);\n    k.data[key_len] = '\\0';\n    k.len = key_len;\n\n    if (mvals) {\n        if (mvals_len > 0) {\n            for (i = 0; i < mvals_len; i++) {\n                p = mvals[i].data;\n                len = mvals[i].len;\n\n                v.data = ngx_palloc(r->pool, len + 1);\n                if (v.data == NULL) {\n                    goto nomem;\n                }\n\n                ngx_memcpy(v.data, p, len);\n                v.data[len] = '\\0';\n                v.len = len;\n\n                if (ngx_http_lua_set_input_header(r, k, v, override && i == 0)\n                    != NGX_OK)\n                {\n                    goto failed;\n                }\n            }\n\n            return NGX_OK;\n        }\n\n        v.data = NULL;\n        v.len = 0;\n\n    } else if (value) {\n        v.data = ngx_palloc(r->pool, value_len + 1);\n        if (v.data == NULL) {\n            goto nomem;\n        }\n\n        ngx_memcpy(v.data, value, value_len);\n        v.data[value_len] = '\\0';\n        v.len = value_len;\n\n    } else {\n        v.data = NULL;\n        v.len = 0;\n    }\n\n    if (ngx_http_lua_set_input_header(r, k, v, override) != NGX_OK) {\n        goto failed;\n    }\n\n    return NGX_OK;\n\nnomem:\n\n    *errmsg = \"no memory\";\n    return NGX_ERROR;\n\nfailed:\n\n    *errmsg = \"failed to set header\";\n    return NGX_ERROR;\n}\n\n\nint\nngx_http_lua_ffi_get_resp_header(ngx_http_request_t *r,\n    const u_char *key, size_t key_len,\n    u_char *key_buf, ngx_http_lua_ffi_str_t *values, int max_nvalues,\n    char **errmsg)\n{\n    int                  found;\n    u_char               c, *p;\n    time_t               last_modified;\n    ngx_uint_t           i;\n    ngx_table_elt_t     *h;\n    ngx_list_part_t     *part;\n    ngx_http_lua_ctx_t  *ctx;\n\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    if (r->connection->fd == (ngx_socket_t) -1) {\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        *errmsg = \"no ctx found\";\n        return NGX_ERROR;\n    }\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n    if (llcf->transform_underscores_in_resp_headers\n        && memchr(key, '_', key_len) != NULL)\n    {\n        for (i = 0; i < key_len; i++) {\n            c = key[i];\n            if (c == '_') {\n                c = '-';\n            }\n\n            key_buf[i] = c;\n        }\n\n    } else {\n        key_buf = (u_char *) key;\n    }\n\n    switch (key_len) {\n    case 14:\n        if (r->headers_out.content_length == NULL\n            && r->headers_out.content_length_n >= 0\n            && ngx_strncasecmp(key_buf, (u_char *) \"Content-Length\", 14) == 0)\n        {\n            p = ngx_palloc(r->pool, NGX_OFF_T_LEN);\n            if (p == NULL) {\n                *errmsg = \"no memory\";\n                return NGX_ERROR;\n            }\n\n            values[0].data = p;\n            values[0].len = (int) (ngx_snprintf(p, NGX_OFF_T_LEN, \"%O\",\n                                              r->headers_out.content_length_n)\n                            - p);\n            return 1;\n        }\n\n        break;\n\n    case 12:\n        if (ngx_strncasecmp(key_buf, (u_char *) \"Content-Type\", 12) == 0\n            && r->headers_out.content_type.len)\n        {\n            values[0].data = r->headers_out.content_type.data;\n            values[0].len = r->headers_out.content_type.len;\n            return 1;\n        }\n\n        break;\n\n    case 13:\n        if (ngx_strncasecmp(key_buf, (u_char *) \"Last-Modified\", 13) == 0) {\n            last_modified = r->headers_out.last_modified_time;\n            if (last_modified >= 0) {\n                p = ngx_palloc(r->pool,\n                               sizeof(\"Mon, 28 Sep 1970 06:00:00 GMT\"));\n                if (p == NULL) {\n                    *errmsg = \"no memory\";\n                    return NGX_ERROR;\n                }\n\n                values[0].data = p;\n                values[0].len = ngx_http_time(p, last_modified) - p;\n\n                return 1;\n            }\n\n            return 0;\n        }\n\n        break;\n\n    default:\n        break;\n    }\n\n    dd(\"not a built-in output header\");\n\n#if 1\n    if (r->headers_out.location\n        && r->headers_out.location->value.len\n        && r->headers_out.location->value.data[0] == '/')\n    {\n        /* XXX ngx_http_core_find_config_phase, for example,\n         * may not initialize the \"key\" and \"hash\" fields\n         * for a nasty optimization purpose, and\n         * we have to work-around it here */\n\n        r->headers_out.location->hash = ngx_http_lua_location_hash;\n        ngx_str_set(&r->headers_out.location->key, \"Location\");\n    }\n#endif\n\n    found = 0;\n\n    part = &r->headers_out.headers.part;\n    h = part->elts;\n\n    for (i = 0; /* void */; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n\n            part = part->next;\n            h = part->elts;\n            i = 0;\n        }\n\n        if (h[i].hash == 0) {\n            continue;\n        }\n\n        dd(\"checking (%d) \\\"%.*s\\\"\", (int) h[i].key.len, (int) h[i].key.len,\n           h[i].key.data);\n\n        if (h[i].key.len == key_len\n            && ngx_strncasecmp(key_buf, h[i].key.data, key_len) == 0)\n        {\n            values[found].data = h[i].value.data;\n            values[found].len = (int) h[i].value.len;\n\n            if (++found >= max_nvalues) {\n                break;\n            }\n        }\n    }\n\n    return found;\n}\n\n\n#if (nginx_version >= 1011011)\nvoid\nngx_http_lua_ngx_raw_header_cleanup(void *data)\n{\n    ngx_http_lua_main_conf_t  *lmcf;\n\n    lmcf = (ngx_http_lua_main_conf_t *) data;\n\n    if (lmcf->busy_buf_ptrs) {\n        ngx_free(lmcf->busy_buf_ptrs);\n        lmcf->busy_buf_ptrs = NULL;\n    }\n}\n#endif\n\n\n#if (NGX_DARWIN)\nint\nngx_http_lua_ffi_set_resp_header_macos(ngx_http_lua_set_resp_header_params_t *p)\n{\n    return ngx_http_lua_ffi_set_resp_header(p->r, (const u_char *) p->key_data,\n                                            p->key_len, p->is_nil,\n                                            (const u_char *) p->sval,\n                                            p->sval_len,\n                                            p->mvals, p->mvals_len,\n                                            p->override, p->errmsg);\n}\n#endif\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_headers.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_HEADERS_H_INCLUDED_\n#define _NGX_HTTP_LUA_HEADERS_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_resp_header_api(lua_State *L);\nvoid ngx_http_lua_inject_req_header_api(lua_State *L);\nvoid ngx_http_lua_create_headers_metatable(ngx_log_t *log, lua_State *L);\n#if (nginx_version >= 1011011)\nvoid ngx_http_lua_ngx_raw_header_cleanup(void *data);\n#endif\n\n\n#endif /* _NGX_HTTP_LUA_HEADERS_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_headers_in.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include <nginx.h>\n#include \"ngx_http_lua_headers_in.h\"\n#include \"ngx_http_lua_util.h\"\n#include <ctype.h>\n\n\nstatic ngx_int_t ngx_http_set_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value,\n    ngx_table_elt_t **output_header);\nstatic ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_set_user_agent_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_set_connection_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_set_builtin_multi_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_lua_validate_host(ngx_str_t *host, ngx_pool_t *pool,\n    ngx_uint_t alloc);\nstatic ngx_int_t ngx_http_set_host_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_lua_rm_header_helper(ngx_list_t *l,\n    ngx_list_part_t *cur, ngx_uint_t i);\n\n\nstatic ngx_http_lua_set_header_t  ngx_http_lua_set_handlers[] = {\n    { ngx_string(\"Host\"),\n                 offsetof(ngx_http_headers_in_t, host),\n                 ngx_http_set_host_header },\n\n    { ngx_string(\"Connection\"),\n                 offsetof(ngx_http_headers_in_t, connection),\n                 ngx_http_set_connection_header },\n\n    { ngx_string(\"If-Modified-Since\"),\n                 offsetof(ngx_http_headers_in_t, if_modified_since),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"If-Unmodified-Since\"),\n                 offsetof(ngx_http_headers_in_t, if_unmodified_since),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"If-Match\"),\n                 offsetof(ngx_http_headers_in_t, if_match),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"If-None-Match\"),\n                 offsetof(ngx_http_headers_in_t, if_none_match),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"User-Agent\"),\n                 offsetof(ngx_http_headers_in_t, user_agent),\n                 ngx_http_set_user_agent_header },\n\n    { ngx_string(\"Referer\"),\n                 offsetof(ngx_http_headers_in_t, referer),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Content-Length\"),\n                 offsetof(ngx_http_headers_in_t, content_length),\n                 ngx_http_set_content_length_header },\n\n    { ngx_string(\"Content-Type\"),\n                 offsetof(ngx_http_headers_in_t, content_type),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Range\"),\n                 offsetof(ngx_http_headers_in_t, range),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"If-Range\"),\n                 offsetof(ngx_http_headers_in_t, if_range),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Transfer-Encoding\"),\n                 offsetof(ngx_http_headers_in_t, transfer_encoding),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Expect\"),\n                 offsetof(ngx_http_headers_in_t, expect),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Upgrade\"),\n                 offsetof(ngx_http_headers_in_t, upgrade),\n                 ngx_http_set_builtin_header },\n\n#if (NGX_HTTP_GZIP)\n    { ngx_string(\"Accept-Encoding\"),\n                 offsetof(ngx_http_headers_in_t, accept_encoding),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Via\"),\n                 offsetof(ngx_http_headers_in_t, via),\n                 ngx_http_set_builtin_header },\n#endif\n\n    { ngx_string(\"Authorization\"),\n                 offsetof(ngx_http_headers_in_t, authorization),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Keep-Alive\"),\n                 offsetof(ngx_http_headers_in_t, keep_alive),\n                 ngx_http_set_builtin_header },\n\n#if (NGX_HTTP_X_FORWARDED_FOR)\n    { ngx_string(\"X-Forwarded-For\"),\n                 offsetof(ngx_http_headers_in_t, x_forwarded_for),\n                 ngx_http_set_builtin_multi_header },\n\n#endif\n\n#if (NGX_HTTP_REALIP)\n    { ngx_string(\"X-Real-IP\"),\n                 offsetof(ngx_http_headers_in_t, x_real_ip),\n                 ngx_http_set_builtin_header },\n#endif\n\n#if (NGX_HTTP_DAV)\n    { ngx_string(\"Depth\"),\n                 offsetof(ngx_http_headers_in_t, depth),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Destination\"),\n                 offsetof(ngx_http_headers_in_t, destination),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Overwrite\"),\n                 offsetof(ngx_http_headers_in_t, overwrite),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Date\"), offsetof(ngx_http_headers_in_t, date),\n                 ngx_http_set_builtin_header },\n#endif\n\n#if defined(nginx_version) && nginx_version >= 1023000\n    { ngx_string(\"Cookie\"),\n                 offsetof(ngx_http_headers_in_t, cookie),\n                 ngx_http_set_builtin_multi_header },\n#else\n    { ngx_string(\"Cookie\"),\n                 offsetof(ngx_http_headers_in_t, cookies),\n                 ngx_http_set_builtin_multi_header },\n#endif\n\n    { ngx_null_string, 0, ngx_http_set_header }\n};\n\n\n/* request time implementation */\n\nstatic ngx_int_t\nngx_http_set_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,\n    ngx_str_t *value)\n{\n    return ngx_http_set_header_helper(r, hv, value, NULL);\n}\n\n\nstatic ngx_int_t\nngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,\n    ngx_str_t *value, ngx_table_elt_t **output_header)\n{\n    ngx_table_elt_t             *h, *matched;\n    ngx_list_part_t             *part;\n    ngx_uint_t                   i;\n    ngx_uint_t                   rc;\n\n    if (hv->no_override) {\n        goto new_header;\n    }\n\n    matched = NULL;\n\nretry:\n\n    part = &r->headers_in.headers.part;\n    h = part->elts;\n\n    for (i = 0; /* void */; i++) {\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n\n            part = part->next;\n            h = part->elts;\n            i = 0;\n        }\n\n        dd(\"i: %d, part: %p\", (int) i, part);\n\n        if (h[i].key.len == hv->key.len\n            && ngx_strncasecmp(h[i].key.data, hv->key.data, h[i].key.len)\n               == 0)\n        {\n            if (value->len == 0 || (matched && matched != &h[i])) {\n                h[i].hash = 0;\n\n                dd(\"rm header %.*s: %.*s\", (int) h[i].key.len, h[i].key.data,\n                   (int) h[i].value.len, h[i].value.data);\n\n                rc = ngx_http_lua_rm_header_helper(&r->headers_in.headers,\n                                                   part, i);\n\n                ngx_http_lua_assert(!(r->headers_in.headers.part.next == NULL\n                                      && r->headers_in.headers.last\n                                         != &r->headers_in.headers.part));\n\n                dd(\"rm header: rc=%d\", (int) rc);\n\n                if (rc == NGX_OK) {\n\n                    if (output_header) {\n                        *output_header = NULL;\n                    }\n\n                    goto retry;\n                }\n\n                return NGX_ERROR;\n            }\n\n            h[i].value = *value;\n\n            if (output_header) {\n                *output_header = &h[i];\n                dd(\"setting existing builtin input header\");\n            }\n\n            if (matched == NULL) {\n                matched = &h[i];\n            }\n        }\n    }\n\n    if (matched){\n        return NGX_OK;\n    }\n\n    if (value->len == 0) {\n        return NGX_OK;\n    }\n\nnew_header:\n\n    h = ngx_list_push(&r->headers_in.headers);\n\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    dd(\"created new header for %.*s\", (int) hv->key.len, hv->key.data);\n\n    if (value->len == 0) {\n        h->hash = 0;\n\n    } else {\n        h->hash = hv->hash;\n    }\n\n    h->key = hv->key;\n    h->value = *value;\n#if defined(nginx_version) && nginx_version >= 1023000\n    h->next = NULL;\n#endif\n\n    h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);\n    if (h->lowcase_key == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_strlow(h->lowcase_key, h->key.data, h->key.len);\n\n    if (output_header) {\n        *output_header = h;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_set_builtin_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    ngx_table_elt_t             *h, **old;\n\n    dd(\"entered set_builtin_header (input)\");\n\n    if (hv->offset) {\n        old = (ngx_table_elt_t **) ((char *) &r->headers_in + hv->offset);\n\n    } else {\n        old = NULL;\n    }\n\n    dd(\"old builtin ptr ptr: %p\", old);\n    if (old) {\n        dd(\"old builtin ptr: %p\", *old);\n    }\n\n    if (old == NULL || *old == NULL) {\n        dd(\"set normal header\");\n        return ngx_http_set_header_helper(r, hv, value, old);\n    }\n\n    h = *old;\n\n    if (value->len == 0) {\n        h->hash = 0;\n        h->value = *value;\n\n        return ngx_http_set_header_helper(r, hv, value, old);\n    }\n\n    h->hash = hv->hash;\n    h->value = *value;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)\n{\n    u_char  *h, ch;\n    size_t   i, dot_pos, host_len;\n\n    enum {\n        sw_usual = 0,\n        sw_literal,\n        sw_rest,\n    } state;\n\n    dot_pos = host->len;\n    host_len = host->len;\n\n    h = host->data;\n\n    state = sw_usual;\n\n    for (i = 0; i < host->len; i++) {\n        ch = h[i];\n\n        switch (ch) {\n\n        case '.':\n            if (dot_pos == i - 1) {\n                return NGX_DECLINED;\n            }\n\n            dot_pos = i;\n            break;\n\n        case ':':\n            if (state == sw_usual) {\n                host_len = i;\n                state = sw_rest;\n            }\n            break;\n\n        case '[':\n            if (i == 0) {\n                state = sw_literal;\n            }\n            break;\n\n        case ']':\n            if (state == sw_literal) {\n                host_len = i + 1;\n                state = sw_rest;\n            }\n            break;\n\n        case '\\0':\n            return NGX_DECLINED;\n\n        default:\n\n            if (ngx_path_separator(ch)) {\n                return NGX_DECLINED;\n            }\n\n            if (ch >= 'A' && ch <= 'Z') {\n                alloc = 1;\n            }\n\n            break;\n        }\n    }\n\n    if (dot_pos == host_len - 1) {\n        host_len--;\n    }\n\n    if (host_len == 0) {\n        return NGX_DECLINED;\n    }\n\n    if (alloc) {\n        host->data = ngx_pnalloc(pool, host_len);\n        if (host->data == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_strlow(host->data, h, host_len);\n    }\n\n    host->len = host_len;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_set_host_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,\n    ngx_str_t *value)\n{\n    ngx_str_t                    host;\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_http_variable_value_t   *var;\n\n    dd(\"server new value len: %d\", (int) value->len);\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    if (value->len) {\n        host= *value;\n\n        if (ngx_http_lua_validate_host(&host, r->pool, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        r->headers_in.server = host;\n\n    } else {\n        r->headers_in.server = *value;\n    }\n\n    var = &r->variables[lmcf->host_var_index];\n    var->valid = 0;\n    var->not_found = 0;\n\n    return ngx_http_set_builtin_header(r, hv, value);\n}\n\n\nstatic ngx_int_t\nngx_http_set_connection_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    r->headers_in.connection_type = 0;\n\n    if (value->len == 0) {\n        return ngx_http_set_builtin_header(r, hv, value);\n    }\n\n    if (ngx_strcasestrn(value->data, \"close\", 5 - 1)) {\n        r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;\n        r->headers_in.keep_alive_n = -1;\n\n    } else if (ngx_strcasestrn(value->data, \"keep-alive\", 10 - 1)) {\n        r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE;\n    }\n\n    return ngx_http_set_builtin_header(r, hv, value);\n}\n\n\n/* borrowed the code from ngx_http_request.c:ngx_http_process_user_agent */\nstatic ngx_int_t\nngx_http_set_user_agent_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    u_char  *user_agent, *msie;\n\n    /* clear existing settings */\n\n    r->headers_in.msie = 0;\n    r->headers_in.msie6 = 0;\n    r->headers_in.opera = 0;\n    r->headers_in.gecko = 0;\n    r->headers_in.chrome = 0;\n    r->headers_in.safari = 0;\n    r->headers_in.konqueror = 0;\n\n    if (value->len == 0) {\n        return ngx_http_set_builtin_header(r, hv, value);\n    }\n\n    /* check some widespread browsers */\n\n    user_agent = value->data;\n\n    msie = ngx_strstrn(user_agent, \"MSIE \", 5 - 1);\n\n    if (msie && msie + 7 < user_agent + value->len) {\n\n        r->headers_in.msie = 1;\n\n        if (msie[6] == '.') {\n\n            switch (msie[5]) {\n            case '4':\n            case '5':\n                r->headers_in.msie6 = 1;\n                break;\n            case '6':\n                if (ngx_strstrn(msie + 8, \"SV1\", 3 - 1) == NULL) {\n                    r->headers_in.msie6 = 1;\n                }\n                break;\n            }\n        }\n    }\n\n    if (ngx_strstrn(user_agent, \"Opera\", 5 - 1)) {\n        r->headers_in.opera = 1;\n        r->headers_in.msie = 0;\n        r->headers_in.msie6 = 0;\n    }\n\n    if (!r->headers_in.msie && !r->headers_in.opera) {\n\n        if (ngx_strstrn(user_agent, \"Gecko/\", 6 - 1)) {\n            r->headers_in.gecko = 1;\n\n        } else if (ngx_strstrn(user_agent, \"Chrome/\", 7 - 1)) {\n            r->headers_in.chrome = 1;\n\n        } else if (ngx_strstrn(user_agent, \"Safari/\", 7 - 1)\n                   && ngx_strstrn(user_agent, \"Mac OS X\", 8 - 1))\n        {\n            r->headers_in.safari = 1;\n\n        } else if (ngx_strstrn(user_agent, \"Konqueror\", 9 - 1)) {\n            r->headers_in.konqueror = 1;\n        }\n    }\n\n    return ngx_http_set_builtin_header(r, hv, value);\n}\n\n\nstatic ngx_int_t\nngx_http_set_content_length_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    off_t           len;\n\n    if (value->len == 0) {\n        return ngx_http_clear_content_length_header(r, hv, value);\n    }\n\n    len = ngx_atoof(value->data, value->len);\n    if (len == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    dd(\"reset headers_in.content_length_n to %d\", (int) len);\n\n    r->headers_in.content_length_n = len;\n\n    return ngx_http_set_builtin_header(r, hv, value);\n}\n\n\nstatic ngx_int_t\nngx_http_set_builtin_multi_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n#if defined(nginx_version) && nginx_version >= 1023000\n    ngx_table_elt_t  **headers, **ph, *h;\n\n    headers = (ngx_table_elt_t **) ((char *) &r->headers_in + hv->offset);\n\n    if (!hv->no_override && *headers != NULL) {\n#if defined(DDEBUG) && (DDEBUG)\n        int  nelts = 0;\n\n        for (h = *headers; h; h = h->next) {\n            nelts++;\n        }\n\n        dd(\"clear multi-value headers: %d\", nelts);\n#endif\n\n        *headers = NULL;\n    }\n\n    if (ngx_http_set_header_helper(r, hv, value, &h) == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (value->len == 0) {\n        return NGX_OK;\n    }\n\n    dd(\"new multi-value header: %p\", h);\n\n    if (*headers) {\n        for (ph = headers; *ph; ph = &(*ph)->next) { /* void */ }\n        *ph = h;\n\n    } else {\n        *headers = h;\n    }\n\n    h->next = NULL;\n\n    return NGX_OK;\n#else\n    ngx_array_t       *headers;\n    ngx_table_elt_t  **v, *h;\n\n    headers = (ngx_array_t *) ((char *) &r->headers_in + hv->offset);\n\n    if (!hv->no_override && headers->nelts > 0) {\n        ngx_array_destroy(headers);\n\n        if (ngx_array_init(headers, r->pool, 2,\n                           sizeof(ngx_table_elt_t *))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        dd(\"clear multi-value headers: %d\", (int) headers->nelts);\n    }\n\n#if 1\n    if (headers->nalloc == 0) {\n        if (ngx_array_init(headers, r->pool, 2,\n                           sizeof(ngx_table_elt_t *))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n#endif\n\n    if (ngx_http_set_header_helper(r, hv, value, &h) == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (value->len == 0) {\n        return NGX_OK;\n    }\n\n    dd(\"new multi-value header: %p\", h);\n\n    v = ngx_array_push(headers);\n    if (v == NULL) {\n        return NGX_ERROR;\n    }\n\n    *v = h;\n    return NGX_OK;\n#endif\n}\n\n\nstatic ngx_int_t\nngx_http_clear_content_length_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    r->headers_in.content_length_n = -1;\n\n    return ngx_http_clear_builtin_header(r, hv, value);\n}\n\n\nstatic ngx_int_t\nngx_http_clear_builtin_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    value->len = 0;\n    return ngx_http_set_builtin_header(r, hv, value);\n}\n\n\nngx_int_t\nngx_http_lua_set_input_header(ngx_http_request_t *r, ngx_str_t key,\n    ngx_str_t value, unsigned override)\n{\n    ngx_http_lua_header_val_t         hv;\n    ngx_http_lua_set_header_t        *handlers = ngx_http_lua_set_handlers;\n    ngx_int_t                         rc;\n    ngx_uint_t                        i;\n\n    dd(\"set header value: %.*s\", (int) value.len, value.data);\n\n    rc = ngx_http_lua_copy_escaped_header(r, &key, 1);\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_http_lua_copy_escaped_header(r, &value, 0);\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (value.len > 0) {\n        hv.hash = ngx_hash_key_lc(key.data, key.len);\n\n    } else {\n        hv.hash = 0;\n    }\n\n    hv.key = key;\n\n    hv.offset = 0;\n    hv.no_override = !override;\n    hv.handler = NULL;\n\n    for (i = 0; handlers[i].name.len; i++) {\n        if (hv.key.len != handlers[i].name.len\n            || ngx_strncasecmp(hv.key.data, handlers[i].name.data,\n                               handlers[i].name.len) != 0)\n        {\n            dd(\"hv key comparison: %s <> %s\", handlers[i].name.data,\n               hv.key.data);\n\n            continue;\n        }\n\n        dd(\"Matched handler: %s %s\", handlers[i].name.data, hv.key.data);\n\n        hv.offset = handlers[i].offset;\n        hv.handler = handlers[i].handler;\n\n        break;\n    }\n\n    if (handlers[i].name.len == 0 && handlers[i].handler) {\n        hv.offset = handlers[i].offset;\n        hv.handler = handlers[i].handler;\n    }\n\n#if 1\n    if (hv.handler == NULL) {\n        return NGX_ERROR;\n    }\n#endif\n\n    if (r->headers_out.status == 400 || r->headers_in.headers.last == NULL) {\n        /* must be a 400 Bad Request */\n        return NGX_OK;\n    }\n\n    return hv.handler(r, &hv, &value);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_rm_header_helper(ngx_list_t *l, ngx_list_part_t *cur,\n    ngx_uint_t i)\n{\n    ngx_table_elt_t             *data;\n    ngx_list_part_t             *new, *part;\n\n    dd(\"list rm item: part %p, i %d, nalloc %d\", cur, (int) i,\n       (int) l->nalloc);\n\n    data = cur->elts;\n\n    dd(\"cur: nelts %d, nalloc %d\", (int) cur->nelts,\n       (int) l->nalloc);\n\n    dd(\"removing: \\\"%.*s:%.*s\\\"\", (int) data[i].key.len, data[i].key.data,\n       (int) data[i].value.len, data[i].value.data);\n\n    if (i == 0) {\n        dd(\"first entry in the part\");\n        cur->elts = (char *) cur->elts + l->size;\n        cur->nelts--;\n\n        if (cur == l->last) {\n            dd(\"being the last part\");\n            if (cur->nelts == 0) {\n#if 1\n                part = &l->part;\n                dd(\"cur=%p, part=%p, part next=%p, last=%p\",\n                   cur, part, part->next, l->last);\n\n                if (part == cur) {\n                    cur->elts = (char *) cur->elts - l->size;\n                    /* do nothing */\n\n                } else {\n                    while (part->next != cur) {\n                        if (part->next == NULL) {\n                            return NGX_ERROR;\n                        }\n\n                        part = part->next;\n                    }\n\n                    l->last = part;\n                    part->next = NULL;\n                    dd(\"part nelts: %d\", (int) part->nelts);\n                    l->nalloc = part->nelts;\n                }\n#endif\n\n            } else {\n                l->nalloc--;\n                dd(\"nalloc decreased: %d\", (int) l->nalloc);\n            }\n\n            return NGX_OK;\n        }\n\n        if (cur->nelts == 0) {\n            dd(\"current part is empty\");\n            part = &l->part;\n            if (part == cur) {\n                ngx_http_lua_assert(cur->next != NULL);\n\n                dd(\"remove 'cur' from the list by rewriting 'cur': \"\n                   \"l->last: %p, cur: %p, cur->next: %p, part: %p\",\n                   l->last, cur, cur->next, part);\n\n                if (l->last == cur->next) {\n                    dd(\"last is cur->next\");\n                    l->part = *(cur->next);\n                    l->last = part;\n                    l->nalloc = part->nelts;\n\n                } else {\n                    l->part = *(cur->next);\n                }\n\n            } else {\n                dd(\"remove 'cur' from the list\");\n                while (part->next != cur) {\n                    if (part->next == NULL) {\n                        return NGX_ERROR;\n                    }\n\n                    part = part->next;\n                }\n\n                part->next = cur->next;\n            }\n\n            return NGX_OK;\n        }\n\n        return NGX_OK;\n    }\n\n    if (i == cur->nelts - 1) {\n        dd(\"last entry in the part\");\n\n        cur->nelts--;\n\n        if (cur == l->last) {\n            l->nalloc--;\n        }\n\n        return NGX_OK;\n    }\n\n    dd(\"the middle entry in the part\");\n\n    new = ngx_palloc(l->pool, sizeof(ngx_list_part_t));\n    if (new == NULL) {\n        return NGX_ERROR;\n    }\n\n    new->elts = &data[i + 1];\n    new->nelts = cur->nelts - i - 1;\n    new->next = cur->next;\n\n    cur->nelts = i;\n    cur->next = new;\n\n    if (cur == l->last) {\n        l->last = new;\n        l->nalloc = new->nelts;\n    }\n\n    return NGX_OK;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_headers_in.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_HEADERS_IN_H_INCLUDED_\n#define _NGX_HTTP_LUA_HEADERS_IN_H_INCLUDED_\n\n\n#include <nginx.h>\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_set_input_header(ngx_http_request_t *r, ngx_str_t key,\n    ngx_str_t value, unsigned override);\n\n\n#endif /* _NGX_HTTP_LUA_HEADERS_IN_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_headers_out.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include <nginx.h>\n#include \"ngx_http_lua_headers_out.h\"\n#include \"ngx_http_lua_util.h\"\n#include <ctype.h>\n\n\nstatic ngx_int_t ngx_http_set_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value,\n    ngx_table_elt_t **output_header, unsigned no_create);\nstatic ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_set_builtin_multi_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_set_last_modified_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_set_content_type_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_clear_last_modified_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_set_location_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value);\n\n\nstatic ngx_http_lua_set_header_t  ngx_http_lua_set_handlers[] = {\n\n    { ngx_string(\"Server\"),\n                 offsetof(ngx_http_headers_out_t, server),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Date\"),\n                 offsetof(ngx_http_headers_out_t, date),\n                 ngx_http_set_builtin_header },\n\n#if 1\n    { ngx_string(\"Content-Encoding\"),\n                 offsetof(ngx_http_headers_out_t, content_encoding),\n                 ngx_http_set_builtin_header },\n#endif\n\n    { ngx_string(\"Location\"),\n                 offsetof(ngx_http_headers_out_t, location),\n                 ngx_http_set_location_header },\n\n    { ngx_string(\"Refresh\"),\n                 offsetof(ngx_http_headers_out_t, refresh),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Last-Modified\"),\n                 offsetof(ngx_http_headers_out_t, last_modified),\n                 ngx_http_set_last_modified_header },\n\n    { ngx_string(\"Content-Range\"),\n                 offsetof(ngx_http_headers_out_t, content_range),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Accept-Ranges\"),\n                 offsetof(ngx_http_headers_out_t, accept_ranges),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"WWW-Authenticate\"),\n                 offsetof(ngx_http_headers_out_t, www_authenticate),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Expires\"),\n                 offsetof(ngx_http_headers_out_t, expires),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"E-Tag\"),\n                 offsetof(ngx_http_headers_out_t, etag),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"ETag\"),\n                 offsetof(ngx_http_headers_out_t, etag),\n                 ngx_http_set_builtin_header },\n\n    { ngx_string(\"Content-Length\"),\n                 offsetof(ngx_http_headers_out_t, content_length),\n                 ngx_http_set_content_length_header },\n\n    { ngx_string(\"Content-Type\"),\n                 offsetof(ngx_http_headers_out_t, content_type),\n                 ngx_http_set_content_type_header },\n\n    { ngx_string(\"Cache-Control\"),\n                 offsetof(ngx_http_headers_out_t, cache_control),\n                 ngx_http_set_builtin_multi_header },\n\n#if (nginx_version >= 1013009)\n    { ngx_string(\"Link\"),\n                 offsetof(ngx_http_headers_out_t, link),\n                 ngx_http_set_builtin_multi_header },\n#endif\n\n    { ngx_null_string, 0, ngx_http_set_header }\n};\n\n\n/* request time implementation */\n\nstatic ngx_int_t\nngx_http_set_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,\n    ngx_str_t *value)\n{\n    return ngx_http_set_header_helper(r, hv, value, NULL, 0);\n}\n\n\nstatic ngx_int_t\nngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,\n    ngx_str_t *value, ngx_table_elt_t **output_header,\n    unsigned no_create)\n{\n    ngx_table_elt_t             *h;\n    ngx_list_part_t             *part;\n    ngx_uint_t                   i;\n    unsigned                     matched = 0;\n\n    if (hv->no_override) {\n        goto new_header;\n    }\n\n#if 1\n    if (r->headers_out.location\n        && r->headers_out.location->value.len\n        && r->headers_out.location->value.data[0] == '/')\n    {\n        /* XXX ngx_http_core_find_config_phase, for example,\n         * may not initialize the \"key\" and \"hash\" fields\n         * for a nasty optimization purpose, and\n         * we have to work-around it here */\n\n        r->headers_out.location->hash = ngx_http_lua_location_hash;\n        ngx_str_set(&r->headers_out.location->key, \"Location\");\n    }\n#endif\n\n    part = &r->headers_out.headers.part;\n    h = part->elts;\n\n    for (i = 0; /* void */; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n\n            part = part->next;\n            h = part->elts;\n            i = 0;\n        }\n\n        if (h[i].hash != 0\n            && h[i].key.len == hv->key.len\n            && ngx_strncasecmp(hv->key.data, h[i].key.data, h[i].key.len) == 0)\n        {\n            dd(\"found out header %.*s\", (int) h[i].key.len, h[i].key.data);\n\n            if (value->len == 0 || matched) {\n                dd(\"clearing normal header for %.*s\", (int) hv->key.len,\n                   hv->key.data);\n\n                h[i].value.len = 0;\n                h[i].hash = 0;\n\n            } else {\n                dd(\"setting header to value %.*s\", (int) value->len,\n                   value->data);\n\n                h[i].value = *value;\n                h[i].hash = hv->hash;\n            }\n\n            if (output_header) {\n                *output_header = &h[i];\n            }\n\n            /* return NGX_OK; */\n            matched = 1;\n        }\n    }\n\n    if (matched){\n        return NGX_OK;\n    }\n\n    if (no_create && value->len == 0) {\n        return NGX_OK;\n    }\n\nnew_header:\n\n    /* XXX we still need to create header slot even if the value\n     * is empty because some builtin headers like Last-Modified\n     * relies on this to get cleared */\n\n    h = ngx_list_push(&r->headers_out.headers);\n\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (value->len == 0) {\n        h->hash = 0;\n\n    } else {\n        h->hash = hv->hash;\n    }\n\n    h->key = hv->key;\n    h->value = *value;\n#if defined(nginx_version) && nginx_version >= 1023000\n    h->next = NULL;\n#endif\n\n    h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);\n    if (h->lowcase_key == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_strlow(h->lowcase_key, h->key.data, h->key.len);\n\n    if (output_header) {\n        *output_header = h;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_set_location_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    ngx_int_t         rc;\n    ngx_table_elt_t  *h;\n\n    rc = ngx_http_set_builtin_header(r, hv, value);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*\n     * we do not set r->headers_out.location here to avoid the handling\n     * the local redirects without a host name by ngx_http_header_filter()\n     */\n\n    h = r->headers_out.location;\n    if (h && h->value.len && h->value.data[0] == '/') {\n        r->headers_out.location = NULL;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_set_builtin_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    ngx_table_elt_t  *h, **old;\n\n    if (hv->offset) {\n        old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);\n\n    } else {\n        old = NULL;\n    }\n\n    if (old == NULL || *old == NULL) {\n        return ngx_http_set_header_helper(r, hv, value, old, 0);\n    }\n\n    h = *old;\n\n    if (value->len == 0) {\n        dd(\"clearing the builtin header\");\n\n        h->hash = 0;\n        h->value = *value;\n\n        return NGX_OK;\n    }\n\n    h->hash = hv->hash;\n    h->key = hv->key;\n    h->value = *value;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_set_builtin_multi_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n#if defined(nginx_version) && nginx_version >= 1023000\n    ngx_table_elt_t  **headers, *h, *ho, **ph;\n\n    headers = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);\n\n    if (hv->no_override) {\n        for (h = *headers; h; h = h->next) {\n            if (!h->hash) {\n                h->value = *value;\n                h->hash = hv->hash;\n                return NGX_OK;\n            }\n        }\n\n        goto create;\n    }\n\n    /* override old values (if any) */\n\n    if (*headers) {\n        for (h = (*headers)->next; h; h = h->next) {\n            h->hash = 0;\n            h->value.len = 0;\n        }\n\n        h = *headers;\n\n        h->value = *value;\n\n        if (value->len == 0) {\n            h->hash = 0;\n\n        } else {\n            h->hash = hv->hash;\n        }\n\n        return NGX_OK;\n    }\n\ncreate:\n\n    for (ph = headers; *ph; ph = &(*ph)->next) { /* void */ }\n\n    ho = ngx_list_push(&r->headers_out.headers);\n    if (ho == NULL) {\n        return NGX_ERROR;\n    }\n\n    ho->value = *value;\n\n    if (value->len == 0) {\n        ho->hash = 0;\n\n    } else {\n        ho->hash = hv->hash;\n    }\n\n    ho->key = hv->key;\n    ho->next = NULL;\n    *ph = ho;\n\n    return NGX_OK;\n#else\n    ngx_array_t      *pa;\n    ngx_table_elt_t  *ho, **ph;\n    ngx_uint_t        i;\n\n    pa = (ngx_array_t *) ((char *) &r->headers_out + hv->offset);\n\n    if (pa->elts == NULL) {\n        if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    if (hv->no_override) {\n        ph = pa->elts;\n        for (i = 0; i < pa->nelts; i++) {\n            if (!ph[i]->hash) {\n                ph[i]->value = *value;\n                ph[i]->hash = hv->hash;\n                return NGX_OK;\n            }\n        }\n\n        goto create;\n    }\n\n    /* override old values (if any) */\n\n    if (pa->nelts > 0) {\n        ph = pa->elts;\n        for (i = 1; i < pa->nelts; i++) {\n            ph[i]->hash = 0;\n            ph[i]->value.len = 0;\n        }\n\n        ph[0]->value = *value;\n\n        if (value->len == 0) {\n            ph[0]->hash = 0;\n\n        } else {\n            ph[0]->hash = hv->hash;\n        }\n\n        return NGX_OK;\n    }\n\ncreate:\n\n    ph = ngx_array_push(pa);\n    if (ph == NULL) {\n        return NGX_ERROR;\n    }\n\n    ho = ngx_list_push(&r->headers_out.headers);\n    if (ho == NULL) {\n        return NGX_ERROR;\n    }\n\n    ho->value = *value;\n\n    if (value->len == 0) {\n        ho->hash = 0;\n\n    } else {\n        ho->hash = hv->hash;\n    }\n\n    ho->key = hv->key;\n    *ph = ho;\n\n    return NGX_OK;\n#endif\n}\n\n\nstatic ngx_int_t\nngx_http_set_content_type_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    ngx_uint_t          i;\n\n    r->headers_out.content_type_len = value->len;\n\n#if 1\n    for (i = 0; i < value->len; i++) {\n        if (value->data[i] == ';') {\n            r->headers_out.content_type_len = i;\n            break;\n        }\n    }\n#endif\n\n    r->headers_out.content_type = *value;\n    r->headers_out.content_type_hash = hv->hash;\n    r->headers_out.content_type_lowcase = NULL;\n\n    value->len = 0;\n\n    return ngx_http_set_header_helper(r, hv, value, NULL, 1);\n}\n\n\nstatic ngx_int_t ngx_http_set_last_modified_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    if (value->len == 0) {\n        return ngx_http_clear_last_modified_header(r, hv, value);\n    }\n\n    r->headers_out.last_modified_time = ngx_http_parse_time(value->data,\n                                                            value->len);\n\n    dd(\"last modified time: %d\", (int) r->headers_out.last_modified_time);\n\n    return ngx_http_set_builtin_header(r, hv, value);\n}\n\n\nstatic ngx_int_t\nngx_http_clear_last_modified_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    r->headers_out.last_modified_time = -1;\n\n    return ngx_http_clear_builtin_header(r, hv, value);\n}\n\n\nstatic ngx_int_t\nngx_http_set_content_length_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    off_t           len;\n\n    if (value->len == 0) {\n        return ngx_http_clear_content_length_header(r, hv, value);\n    }\n\n    len = ngx_atoof(value->data, value->len);\n    if (len == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    r->headers_out.content_length_n = len;\n\n    return ngx_http_set_builtin_header(r, hv, value);\n}\n\n\nstatic ngx_int_t\nngx_http_clear_content_length_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    r->headers_out.content_length_n = -1;\n\n    return ngx_http_clear_builtin_header(r, hv, value);\n}\n\n\nstatic ngx_int_t\nngx_http_clear_builtin_header(ngx_http_request_t *r,\n    ngx_http_lua_header_val_t *hv, ngx_str_t *value)\n{\n    value->len = 0;\n\n    return ngx_http_set_builtin_header(r, hv, value);\n}\n\n\nngx_int_t\nngx_http_lua_set_output_header(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx,\n    ngx_str_t key, ngx_str_t value, unsigned override)\n{\n    ngx_http_lua_header_val_t         hv;\n    ngx_http_lua_main_conf_t         *lmcf;\n    ngx_http_lua_set_header_t        *lsh;\n    ngx_hash_t                       *hash;\n\n    dd(\"set header value: %.*s\", (int) value.len, value.data);\n\n    if (ngx_http_lua_copy_escaped_header(r, &key, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_lua_copy_escaped_header(r, &value, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    hv.hash = ngx_hash_key_lc(key.data, key.len);\n    hv.key = key;\n\n    hv.offset = 0;\n    hv.no_override = !override;\n    hv.handler = ngx_http_set_header;\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n    hash = &lmcf->builtin_headers_out;\n    lsh = ngx_http_lua_hash_find_lc(hash, hv.hash, hv.key.data, hv.key.len);\n    if (lsh) {\n        dd(\"Matched handler: %s %s\", lsh->name.data, hv.key.data);\n        hv.offset = lsh->offset;\n        hv.handler = lsh->handler;\n        if (hv.handler == ngx_http_set_content_type_header) {\n            ctx->mime_set = 1;\n        }\n    }\n\n    return hv.handler(r, &hv, &value);\n}\n\n\nint\nngx_http_lua_get_output_header(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, ngx_str_t *key)\n{\n    ngx_table_elt_t            *h;\n    ngx_list_part_t            *part;\n    ngx_uint_t                  i;\n    unsigned                    found;\n\n    dd(\"looking for response header \\\"%.*s\\\"\", (int) key->len, key->data);\n\n    switch (key->len) {\n    case 14:\n        if (r->headers_out.content_length == NULL\n            && r->headers_out.content_length_n >= 0\n            && ngx_strncasecmp(key->data, (u_char *) \"Content-Length\", 14) == 0)\n        {\n            lua_pushinteger(L, (lua_Integer) r->headers_out.content_length_n);\n            return 1;\n        }\n\n        break;\n\n    case 12:\n        if (ngx_strncasecmp(key->data, (u_char *) \"Content-Type\", 12) == 0\n            && r->headers_out.content_type.len)\n        {\n            lua_pushlstring(L, (char *) r->headers_out.content_type.data,\n                            r->headers_out.content_type.len);\n            return 1;\n        }\n\n        break;\n\n    default:\n        break;\n    }\n\n    dd(\"not a built-in output header\");\n\n    found = 0;\n\n#if 1\n    if (r->headers_out.location\n        && r->headers_out.location->value.len\n        && r->headers_out.location->value.data[0] == '/')\n    {\n        /* XXX ngx_http_core_find_config_phase, for example,\n         * may not initialize the \"key\" and \"hash\" fields\n         * for a nasty optimization purpose, and\n         * we have to work-around it here */\n\n        r->headers_out.location->hash = ngx_http_lua_location_hash;\n        ngx_str_set(&r->headers_out.location->key, \"Location\");\n    }\n#endif\n\n    part = &r->headers_out.headers.part;\n    h = part->elts;\n\n    for (i = 0; /* void */; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n\n            part = part->next;\n            h = part->elts;\n            i = 0;\n        }\n\n        if (h[i].hash == 0) {\n            continue;\n        }\n\n        if (h[i].hash != 0\n            && h[i].key.len == key->len\n            && ngx_strncasecmp(key->data, h[i].key.data, h[i].key.len) == 0)\n         {\n             if (!found) {\n                 found = 1;\n\n                 lua_pushlstring(L, (char *) h[i].value.data, h[i].value.len);\n                 continue;\n             }\n\n             if (found == 1) {\n                 lua_createtable(L, 4 /* narr */, 0);\n                 lua_insert(L, -2);\n                 lua_rawseti(L, -2, found);\n             }\n\n             found++;\n\n             lua_pushlstring(L, (char *) h[i].value.data, h[i].value.len);\n             lua_rawseti(L, -2, found);\n         }\n    }\n\n    if (found) {\n        return 1;\n    }\n\n    lua_pushnil(L);\n    return 1;\n}\n\n\nngx_int_t\nngx_http_lua_init_builtin_headers_out(ngx_conf_t *cf,\n    ngx_http_lua_main_conf_t *lmcf)\n{\n    ngx_array_t                   headers;\n    ngx_hash_key_t               *hk;\n    ngx_hash_init_t               hash;\n    ngx_http_lua_set_header_t    *handlers = ngx_http_lua_set_handlers;\n    ngx_uint_t                    count;\n\n    count = sizeof(ngx_http_lua_set_handlers)\n            / sizeof(ngx_http_lua_set_header_t);\n\n    if (ngx_array_init(&headers, cf->temp_pool, count, sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    while (handlers->name.data) {\n        hk = ngx_array_push(&headers);\n        if (hk == NULL) {\n            return NGX_ERROR;\n        }\n\n        hk->key = handlers->name;\n        hk->key_hash = ngx_hash_key_lc(handlers->name.data, handlers->name.len);\n        hk->value = (void *) handlers;\n\n        handlers++;\n    }\n\n    hash.hash = &lmcf->builtin_headers_out;\n    hash.key = ngx_hash_key_lc;\n    hash.max_size = 512;\n    hash.bucket_size = ngx_align(64, ngx_cacheline_size);\n    hash.name = \"builtin_headers_out_hash\";\n    hash.pool = cf->pool;\n    hash.temp_pool = NULL;\n\n    return ngx_hash_init(&hash, headers.elts, headers.nelts);\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_headers_out.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_HEADERS_OUT_H_INCLUDED_\n#define _NGX_HTTP_LUA_HEADERS_OUT_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\n#if (NGX_DARWIN)\ntypedef struct {\n    ngx_http_request_t   *r;\n    const char           *key_data;\n    size_t                key_len;\n    int                   is_nil;\n    const char           *sval;\n    size_t                sval_len;\n    void                 *mvals;\n    size_t                mvals_len;\n    int                   override;\n    char                **errmsg;\n} ngx_http_lua_set_resp_header_params_t;\n#endif\n\n\nngx_int_t ngx_http_lua_set_output_header(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, ngx_str_t key, ngx_str_t value, unsigned override);\nint ngx_http_lua_get_output_header(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, ngx_str_t *key);\nngx_int_t ngx_http_lua_init_builtin_headers_out(ngx_conf_t *cf,\n    ngx_http_lua_main_conf_t *lmcf);\n\n\n#endif /* _NGX_HTTP_LUA_HEADERS_OUT_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_initby.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n\n#include \"ddebug.h\"\n#include \"ngx_http_lua_initby.h\"\n#include \"ngx_http_lua_util.h\"\n\n\nngx_int_t\nngx_http_lua_init_by_inline(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf,\n    lua_State *L)\n{\n    int         status;\n    const char *chunkname;\n\n\n    if (lmcf->init_chunkname == NULL) {\n        chunkname = \"=init_by_lua\";\n\n    } else {\n        chunkname = (const char *) lmcf->init_chunkname;\n    }\n\n    status = luaL_loadbuffer(L, (char *) lmcf->init_src.data,\n                             lmcf->init_src.len, chunkname)\n             || ngx_http_lua_do_call(log, L);\n\n    return ngx_http_lua_report(log, L, status, \"init_by_lua\");\n}\n\n\nngx_int_t\nngx_http_lua_init_by_file(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf,\n    lua_State *L)\n{\n    int         status;\n\n    status = luaL_loadfile(L, (char *) lmcf->init_src.data)\n             || ngx_http_lua_do_call(log, L);\n\n    return ngx_http_lua_report(log, L, status, \"init_by_lua_file\");\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_initby.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_INITBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_INITBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_init_by_inline(ngx_log_t *log,\n    ngx_http_lua_main_conf_t *lmcf, lua_State *L);\n\nngx_int_t ngx_http_lua_init_by_file(ngx_log_t *log,\n    ngx_http_lua_main_conf_t *lmcf, lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_INITBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_initworkerby.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_initworkerby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_pipe.h\"\n\n\nstatic u_char *ngx_http_lua_log_init_worker_error(ngx_log_t *log,\n    u_char *buf, size_t len);\n\n\nngx_int_t\nngx_http_lua_init_worker(ngx_cycle_t *cycle)\n{\n    char                        *rv;\n    void                        *cur, *prev;\n    ngx_uint_t                   i;\n    ngx_conf_t                   conf;\n    ngx_conf_file_t              cf_file;\n    ngx_cycle_t                 *fake_cycle;\n    ngx_module_t               **modules;\n    ngx_open_file_t             *file, *ofile;\n    ngx_list_part_t             *part;\n    ngx_connection_t            *c = NULL;\n    ngx_http_module_t           *module;\n    ngx_http_request_t          *r = NULL;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_conf_ctx_t         *conf_ctx, http_ctx;\n    ngx_http_lua_loc_conf_t     *top_llcf;\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_http_core_loc_conf_t    *clcf, *top_clcf;\n\n    lmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_lua_module);\n\n    if (lmcf == NULL || lmcf->lua == NULL) {\n        return NGX_OK;\n    }\n\n    /* lmcf != NULL && lmcf->lua != NULL */\n\n#if !(NGX_WIN32)\n    if (ngx_process == NGX_PROCESS_HELPER\n#   ifdef HAVE_PRIVILEGED_PROCESS_PATCH\n        && !ngx_is_privileged_agent\n#   endif\n       )\n    {\n        /* disable init_worker_by_lua* and destroy lua VM in cache processes */\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua close the global Lua VM %p in the \"\n                       \"cache helper process %P\", lmcf->lua, ngx_pid);\n\n        lmcf->vm_cleanup->handler(lmcf->vm_cleanup->data);\n        lmcf->vm_cleanup->handler = NULL;\n\n        return NGX_OK;\n    }\n\n#   ifdef HAVE_NGX_LUA_PIPE\n    if (ngx_http_lua_pipe_add_signal_handler(cycle) != NGX_OK) {\n        return NGX_ERROR;\n    }\n#   endif\n\n#endif  /* NGX_WIN32 */\n\n#if (NGX_HTTP_LUA_HAVE_SA_RESTART)\n    if (lmcf->set_sa_restart) {\n        ngx_http_lua_set_sa_restart(ngx_cycle->log);\n    }\n#endif\n\n    if (lmcf->init_worker_handler == NULL) {\n        return NGX_OK;\n    }\n\n    conf_ctx = (ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index];\n    http_ctx.main_conf = conf_ctx->main_conf;\n\n    top_clcf = conf_ctx->loc_conf[ngx_http_core_module.ctx_index];\n    top_llcf = conf_ctx->loc_conf[ngx_http_lua_module.ctx_index];\n\n    ngx_memzero(&conf, sizeof(ngx_conf_t));\n\n    conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, cycle->log);\n    if (conf.temp_pool == NULL) {\n        return NGX_ERROR;\n    }\n\n    conf.temp_pool->log = cycle->log;\n\n    /* we fake a temporary ngx_cycle_t here because some\n     * modules' merge conf handler may produce side effects in\n     * cf->cycle (like ngx_proxy vs cf->cycle->paths).\n     * also, we cannot allocate our temp cycle on the stack\n     * because some modules like ngx_http_core_module reference\n     * addresses within cf->cycle (i.e., via \"&cf->cycle->new_log\")\n     */\n\n    fake_cycle = ngx_palloc(cycle->pool, sizeof(ngx_cycle_t));\n    if (fake_cycle == NULL) {\n        goto failed;\n    }\n\n    ngx_memcpy(fake_cycle, cycle, sizeof(ngx_cycle_t));\n\n    /*\n     * nginx clears cycle->old_cycle after ngx_init_cycle() completes.\n     * Since nginx 1.29.2, ngx_ssl_cache_fetch() accesses old_cycle->conf_ctx\n     * without a NULL guard, so we must ensure old_cycle is non-NULL.\n     * This avoids a NULL dereference when merge_loc_conf triggers\n     * ngx_ssl_trusted_certificate.\n     * Pointing to the current cycle is safe: the SSL cache is shared via\n     * conf_ctx, so cert lookups still find previously loaded entries.\n     */\n    if (fake_cycle->old_cycle == NULL) {\n        fake_cycle->old_cycle = cycle;\n    }\n\n    ngx_queue_init(&fake_cycle->reusable_connections_queue);\n\n    if (ngx_array_init(&fake_cycle->listening, cycle->pool,\n                       cycle->listening.nelts ? cycle->listening.nelts : 1,\n                       sizeof(ngx_listening_t))\n        != NGX_OK)\n    {\n        goto failed;\n    }\n\n    if (ngx_array_init(&fake_cycle->paths, cycle->pool,\n                       cycle->paths.nelts ? cycle->paths.nelts : 1,\n                       sizeof(ngx_path_t *))\n        != NGX_OK)\n    {\n        goto failed;\n    }\n\n    part = &cycle->open_files.part;\n    ofile = part->elts;\n\n    if (ngx_list_init(&fake_cycle->open_files, cycle->pool,\n                      part->nelts ? part->nelts : 1,\n                      sizeof(ngx_open_file_t))\n        != NGX_OK)\n    {\n        goto failed;\n    }\n\n    for (i = 0; /* void */ ; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n\n            part = part->next;\n            ofile = part->elts;\n            i = 0;\n        }\n\n        file = ngx_list_push(&fake_cycle->open_files);\n        if (file == NULL) {\n            goto failed;\n        }\n\n        ngx_memcpy(file, ofile, sizeof(ngx_open_file_t));\n    }\n\n    if (ngx_list_init(&fake_cycle->shared_memory, cycle->pool, 1,\n                      sizeof(ngx_shm_zone_t))\n        != NGX_OK)\n    {\n        goto failed;\n    }\n\n    conf.ctx = &http_ctx;\n    conf.cycle = fake_cycle;\n    conf.pool = fake_cycle->pool;\n    conf.log = cycle->log;\n\n    ngx_memzero(&cf_file, sizeof(cf_file));\n    cf_file.file.name = cycle->conf_file;\n    conf.conf_file = &cf_file;\n\n    http_ctx.loc_conf = ngx_pcalloc(conf.pool,\n                                    sizeof(void *) * ngx_http_max_module);\n    if (http_ctx.loc_conf == NULL) {\n        return NGX_ERROR;\n    }\n\n    http_ctx.srv_conf = ngx_pcalloc(conf.pool,\n                                    sizeof(void *) * ngx_http_max_module);\n    if (http_ctx.srv_conf == NULL) {\n        return NGX_ERROR;\n    }\n\n#if (nginx_version >= 1009011)\n    modules = cycle->modules;\n#else\n    modules = ngx_modules;\n#endif\n\n    for (i = 0; modules[i]; i++) {\n        if (modules[i]->type != NGX_HTTP_MODULE) {\n            continue;\n        }\n\n        module = modules[i]->ctx;\n\n        if (module->create_srv_conf) {\n            cur = module->create_srv_conf(&conf);\n            if (cur == NULL) {\n                return NGX_ERROR;\n            }\n\n            http_ctx.srv_conf[modules[i]->ctx_index] = cur;\n\n            if (module->merge_srv_conf) {\n                prev = module->create_srv_conf(&conf);\n                if (prev == NULL) {\n                    return NGX_ERROR;\n                }\n\n                rv = module->merge_srv_conf(&conf, prev, cur);\n                if (rv != NGX_CONF_OK) {\n                    goto failed;\n                }\n            }\n        }\n\n        if (module->create_loc_conf) {\n            cur = module->create_loc_conf(&conf);\n            if (cur == NULL) {\n                return NGX_ERROR;\n            }\n\n            http_ctx.loc_conf[modules[i]->ctx_index] = cur;\n\n            if (module->merge_loc_conf) {\n                if (modules[i] == &ngx_http_lua_module) {\n                    prev = top_llcf;\n\n                } else if (modules[i] == &ngx_http_core_module) {\n                    prev = top_clcf;\n\n                } else {\n                    prev = module->create_loc_conf(&conf);\n                    if (prev == NULL) {\n                        return NGX_ERROR;\n                    }\n                }\n\n                rv = module->merge_loc_conf(&conf, prev, cur);\n                if (rv != NGX_CONF_OK) {\n                    goto failed;\n                }\n            }\n        }\n    }\n\n    ngx_destroy_pool(conf.temp_pool);\n    conf.temp_pool = NULL;\n\n    c = ngx_http_lua_create_fake_connection(NULL);\n    if (c == NULL) {\n        goto failed;\n    }\n\n    c->log->handler = ngx_http_lua_log_init_worker_error;\n\n    r = ngx_http_lua_create_fake_request(c);\n    if (r == NULL) {\n        goto failed;\n    }\n\n    r->main_conf = http_ctx.main_conf;\n    r->srv_conf = http_ctx.srv_conf;\n    r->loc_conf = http_ctx.loc_conf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n#if (nginx_version >= 1009000)\n    ngx_set_connection_log(r->connection, clcf->error_log);\n\n#else\n    ngx_http_set_connection_log(r->connection, clcf->error_log);\n#endif\n\n    ctx = ngx_http_lua_create_ctx(r);\n    if (ctx == NULL) {\n        goto failed;\n    }\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_INIT_WORKER;\n    ctx->cur_co_ctx = NULL;\n    r->read_event_handler = ngx_http_block_reading;\n\n    ngx_http_lua_set_req(lmcf->lua, r);\n\n    (void) lmcf->init_worker_handler(cycle->log, lmcf, lmcf->lua);\n\n    ngx_http_lua_set_req(lmcf->lua, NULL);\n\n    ngx_destroy_pool(c->pool);\n    return NGX_OK;\n\nfailed:\n\n    if (conf.temp_pool) {\n        ngx_destroy_pool(conf.temp_pool);\n    }\n\n    if (c) {\n        ngx_http_lua_close_fake_connection(c);\n    }\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_http_lua_init_worker_by_inline(ngx_log_t *log,\n    ngx_http_lua_main_conf_t *lmcf, lua_State *L)\n{\n    int         status;\n    const char *chunkname;\n\n    if (lmcf->init_worker_chunkname == NULL) {\n        chunkname = \"=init_worker_by_lua\";\n\n    } else {\n        chunkname = (const char *) lmcf->init_worker_chunkname;\n    }\n\n    status = luaL_loadbuffer(L, (char *) lmcf->init_worker_src.data,\n                             lmcf->init_worker_src.len, chunkname)\n             || ngx_http_lua_do_call(log, L);\n\n    return ngx_http_lua_report(log, L, status, \"init_worker_by_lua\");\n}\n\n\nngx_int_t\nngx_http_lua_init_worker_by_file(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf,\n    lua_State *L)\n{\n    int         status;\n\n    status = luaL_loadfile(L, (char *) lmcf->init_worker_src.data)\n             || ngx_http_lua_do_call(log, L);\n\n    return ngx_http_lua_report(log, L, status, \"init_worker_by_lua_file\");\n}\n\n\nstatic u_char *\nngx_http_lua_log_init_worker_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char              *p;\n\n    if (log->action) {\n        p = ngx_snprintf(buf, len, \" while %s\", log->action);\n        len -= p - buf;\n        buf = p;\n    }\n\n    return ngx_snprintf(buf, len, \", context: init_worker_by_lua*\");\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_initworkerby.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_INITWORKERBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_INITWORKERBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_init_worker_by_inline(ngx_log_t *log,\n    ngx_http_lua_main_conf_t *lmcf, lua_State *L);\n\nngx_int_t ngx_http_lua_init_worker_by_file(ngx_log_t *log,\n    ngx_http_lua_main_conf_t *lmcf, lua_State *L);\n\nngx_int_t ngx_http_lua_init_worker(ngx_cycle_t *cycle);\n\n\n#endif /* _NGX_HTTP_LUA_INITWORKERBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_input_filters.c",
    "content": "\n/*\n * Copyright (C) by OpenResty Inc.\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t\nngx_http_lua_read_bytes(ngx_buf_t *src, ngx_chain_t *buf_in, size_t *rest,\n    ssize_t bytes, ngx_log_t *log)\n{\n    if (bytes == 0) {\n        return NGX_ERROR;\n    }\n\n    if ((size_t) bytes >= *rest) {\n\n        buf_in->buf->last += *rest;\n        src->pos += *rest;\n        *rest = 0;\n\n        return NGX_OK;\n    }\n\n    /* bytes < *rest */\n\n    buf_in->buf->last += bytes;\n    src->pos += bytes;\n    *rest -= bytes;\n\n    return NGX_AGAIN;\n}\n\n\nngx_int_t\nngx_http_lua_read_all(ngx_buf_t *src, ngx_chain_t *buf_in, ssize_t bytes,\n    ngx_log_t *log)\n{\n    if (bytes == 0) {\n        return NGX_OK;\n    }\n\n    buf_in->buf->last += bytes;\n    src->pos += bytes;\n\n    return NGX_AGAIN;\n}\n\n\nngx_int_t\nngx_http_lua_read_any(ngx_buf_t *src, ngx_chain_t *buf_in, size_t *max,\n    ssize_t bytes, ngx_log_t *log)\n{\n    if (bytes == 0) {\n        return NGX_ERROR;\n    }\n\n    if (bytes >= (ssize_t) *max) {\n        bytes = (ssize_t) *max;\n    }\n\n    buf_in->buf->last += bytes;\n    src->pos += bytes;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_lua_read_line(ngx_buf_t *src, ngx_chain_t *buf_in, ssize_t bytes,\n    ngx_log_t *log)\n{\n    u_char                      *dst;\n    u_char                       c;\n#if (NGX_DEBUG)\n    u_char                      *begin;\n#endif\n\n#if (NGX_DEBUG)\n    begin = src->pos;\n#endif\n\n    if (bytes == 0) {\n        return NGX_ERROR;\n    }\n\n    dd(\"already read: %p: %.*s\", buf_in,\n       (int) (buf_in->buf->last - buf_in->buf->pos), buf_in->buf->pos);\n\n    dd(\"data read: %.*s\", (int) bytes, src->pos);\n\n    dst = buf_in->buf->last;\n\n    while (bytes--) {\n\n        c = *src->pos++;\n\n        switch (c) {\n        case '\\n':\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                           \"lua read the final line part: \\\"%*s\\\"\",\n                           src->pos - 1 - begin, begin);\n\n            buf_in->buf->last = dst;\n\n            dd(\"read a line: %p: %.*s\", buf_in,\n               (int) (buf_in->buf->last - buf_in->buf->pos), buf_in->buf->pos);\n\n            return NGX_OK;\n\n        case '\\r':\n            /* ignore it */\n            break;\n\n        default:\n            *dst++ = c;\n            break;\n        }\n    }\n\n#if (NGX_DEBUG)\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"lua read partial line data: %*s\", dst - begin, begin);\n#endif\n\n    buf_in->buf->last = dst;\n\n    return NGX_AGAIN;\n}\n"
  },
  {
    "path": "src/ngx_http_lua_input_filters.h",
    "content": "\n/*\n * Copyright (C) by OpenResty Inc.\n */\n\n\n#ifndef _NGX_HTTP_LUA_INPUT_FILTERS_H_INCLUDED_\n#define _NGX_HTTP_LUA_INPUT_FILTERS_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_read_bytes(ngx_buf_t *src, ngx_chain_t *buf_in,\n    size_t *rest, ssize_t bytes, ngx_log_t *log);\n\nngx_int_t ngx_http_lua_read_all(ngx_buf_t *src, ngx_chain_t *buf_in,\n    ssize_t bytes, ngx_log_t *log);\n\nngx_int_t ngx_http_lua_read_any(ngx_buf_t *src, ngx_chain_t *buf_in,\n    size_t *max, ssize_t bytes, ngx_log_t *log);\n\nngx_int_t ngx_http_lua_read_line(ngx_buf_t *src, ngx_chain_t *buf_in,\n    ssize_t bytes, ngx_log_t *log);\n\n\n#endif /* _NGX_HTTP_LUA_INPUT_FILTERS_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_lex.c",
    "content": "/*\n * Copyright (C) Yichun Zhang (agentzh)\n *\n * WARNING: DO NOT EVER EDIT THIS FILE!!\n *\n * This file was automatically generated by the OpenResty Regex compiler.\n */\n\n\n#include \"ngx_http_lua_lex.h\"\n#include <stdlib.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <ctype.h>\n#include <errno.h>\n#include <string.h>\n\n\n#if __GNUC__ > 3\n#    define likely(x)       __builtin_expect((x),1)\n#    define unlikely(x)     __builtin_expect((x),0)\n#else\n#    define likely(x)      (x)\n#    define unlikely(x)    (x)\n#endif\n\n\n#ifndef u_char\n#define u_char          unsigned char\n#endif\n\n\nenum {\n    NO_MATCH = -1,\n};\n\n\n/*\n * ngx_http_lua_lex: the \"ovec\" array should be allocated by the caller with at\n * least 2 elements.\n */\nint\nngx_http_lua_lex(const u_char *const s, size_t len, int *const ovec)\n{\n    unsigned i = 0;\n    int matched_0 = -1;\n    int matched_1 = -1;\n    int matched_id = NO_MATCH;  /* (pending) matched regex ID */\n    int c;\n    int caps0_0 = -1;\n    int caps0_10 = -1;\n    int caps0_12 = -1;\n    int caps0_14 = -1;\n    int caps0_2 = -1;\n    int caps0_4 = -1;\n    int caps0_6 = -1;\n    int caps0_8 = -1;\n    int caps1_0 = -1;\n    int caps1_10 = -1;\n    int caps1_12 = -1;\n    int caps1_14 = -1;\n    int caps1_2 = -1;\n    int caps1_4 = -1;\n    int caps1_6 = -1;\n    int caps1_8 = -1;\n    int caps2_0 = -1;\n    int caps2_10 = -1;\n    int caps2_2 = -1;\n    int caps2_4 = -1;\n    int caps2_6 = -1;\n    int caps2_8 = -1;\n    int caps3_10 = -1;\n\n    {  /* DFA node {0} 0 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st0_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 34: {\n        /* transfer caps from row 0 to row 1 */\n        /* capture stores */\n        caps0_12 = i - 1;\n        goto st2;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 0 to row 1 */\n        /* capture stores */\n        caps0_14 = i - 1;\n        goto st3;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 0 to row 1 */\n        /* transfer caps from row 0 to row 2 */\n        /* capture stores */\n        caps0_6 = i - 1;\n        caps1_10 = i - 1;\n        goto st4;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 0 to row 1 */\n        /* capture stores */\n        caps0_4 = i - 1;\n        goto st5;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 0 to row 1 */\n        /* capture stores */\n        caps0_8 = i - 1;\n        goto st6;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 0 to row 1 */\n        /* capture stores */\n        caps0_0 = i - 1;\n        goto st7;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 0 to row 1 */\n        /* capture stores */\n        caps0_2 = i - 1;\n        goto st8;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c == 92)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st1;\n    }  /* end state */\n\n    goto st0_error;\n\nst1: {  /* DFA node {1} 1 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st1_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 34: {\n        /* transfer caps from row 0 to row 1 */\n        /* capture stores */\n        caps0_12 = i - 1;\n        goto st2;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 0 to row 1 */\n        /* capture stores */\n        caps0_14 = i - 1;\n        goto st3;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 0 to row 1 */\n        /* transfer caps from row 0 to row 2 */\n        /* capture stores */\n        caps0_6 = i - 1;\n        caps1_10 = i - 1;\n        goto st4;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 0 to row 1 */\n        /* capture stores */\n        caps0_4 = i - 1;\n        goto st5;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 0 to row 1 */\n        /* capture stores */\n        caps0_8 = i - 1;\n        goto st6;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 0 to row 1 */\n        /* capture stores */\n        caps0_0 = i - 1;\n        goto st7;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 0 to row 1 */\n        /* capture stores */\n        caps0_2 = i - 1;\n        goto st8;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c == 92)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st1;\n    }  /* end state */\n\n    goto st1_error;\n\nst2: {  /* DFA node {59,1} 2 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st2_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 1 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st10;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st11;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 1 to row 2 */\n        /* transfer caps from row 1 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st12;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st13;\n        break;\n        }\n    case 92: {\n        goto st14;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st15;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st16;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st17;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st9;\n    }  /* end state */\n\n    goto st2_error;\n\nst3: {  /* DFA node {72,1} 3 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st3_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 1 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st19;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st20;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 1 to row 2 */\n        /* transfer caps from row 1 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st21;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st22;\n        break;\n        }\n    case 92: {\n        goto st23;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st24;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st25;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st26;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st18;\n    }  /* end state */\n\n    goto st3_error;\n\nst4: {  /* DFA node {30,50,1} 4 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st4_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 34: {\n        /* transfer caps from row 2 to row 0 */\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps0_12 = i - 1;\n        goto st2;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 0 */\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps0_14 = i - 1;\n        goto st3;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 3 */\n        /* transfer caps from row 2 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st27;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 0 */\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps0_4 = i - 1;\n        goto st5;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 0 */\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps0_8 = i - 1;\n        goto st6;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 0 */\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps0_0 = i - 1;\n        goto st7;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 0 */\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps0_2 = i - 1;\n        goto st8;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c == 92)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 0 */\n    goto st1;\n    }  /* end state */\n\n    goto st4_error;\n\nst5: {  /* DFA node {21,1} 5 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st5_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 34: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_12 = i - 1;\n        goto st2;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_14 = i - 1;\n        goto st3;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 1 to row 0 */\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps0_6 = i - 1;\n        caps1_10 = i - 1;\n        goto st4;\n        break;\n        }\n    case 61: {\n        goto st28;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st29;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_8 = i - 1;\n        goto st6;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_0 = i - 1;\n        goto st7;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_2 = i - 1;\n        goto st8;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c == 92)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 1 to row 0 */\n    goto st1;\n    }  /* end state */\n\n    goto st5_error;\n\nst6: {  /* DFA node {41,1} 6 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st6_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 34: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_12 = i - 1;\n        goto st2;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_14 = i - 1;\n        goto st3;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 1 to row 0 */\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps0_6 = i - 1;\n        caps1_10 = i - 1;\n        goto st4;\n        break;\n        }\n    case 61: {\n        goto st30;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_4 = i - 1;\n        goto st5;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st31;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_0 = i - 1;\n        goto st7;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_2 = i - 1;\n        goto st8;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c == 92)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 1 to row 0 */\n    goto st1;\n    }  /* end state */\n\n    goto st6_error;\n\nst7: {  /* DFA node {11,1} 7 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_0;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 0;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst8: {  /* DFA node {16,1} 8 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_2;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 1;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst9: {  /* DFA node {65,1} 9 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st9_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 1 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st10;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st11;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 1 to row 2 */\n        /* transfer caps from row 1 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st12;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st13;\n        break;\n        }\n    case 92: {\n        goto st14;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st15;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st16;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st17;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st9;\n    }  /* end state */\n\n    goto st9_error;\n\nst10: {  /* DFA node {67,59,1} 10 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_12;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 6;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst11: {  /* DFA node {65,72,1} 11 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st11_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 3 */\n        /* transfer caps from row 2 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st38;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st39;\n        break;\n        }\n    case 92: {\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st41;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st35;\n    }  /* end state */\n\n    goto st11_error;\n\nst12: {  /* DFA node {65,30,50,1} 12 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st12_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st10;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st11;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 4 */\n        /* transfer caps from row 3 to row 5 */\n        /* capture stores */\n        goto st44;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st13;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 1 */\n        goto st14;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st15;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st16;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st17;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 1 */\n    goto st9;\n    }  /* end state */\n\n    goto st12_error;\n\nst13: {  /* DFA node {65,21,1} 13 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st13_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st10;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st11;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st12;\n        break;\n        }\n    case 61: {\n        goto st45;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st46;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st14;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st15;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st16;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st17;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st9;\n    }  /* end state */\n\n    goto st13_error;\n\nst14: {  /* DFA node {62,1} 14 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st14_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 1 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st48;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st49;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 1 to row 2 */\n        /* transfer caps from row 1 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st50;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st51;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st52;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st53;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st54;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c == 92)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st47;\n    }  /* end state */\n\n    goto st14_error;\n\nst15: {  /* DFA node {65,41,1} 15 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st15_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st10;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st11;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st12;\n        break;\n        }\n    case 61: {\n        goto st55;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st13;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st14;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st56;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st16;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st17;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st9;\n    }  /* end state */\n\n    goto st15_error;\n\nst16: {  /* DFA node {65,11,1} 16 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_0;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 0;\n    if (c != -1) {\n        if (c == 34) {\n            goto st58;\n        }\n        if (c == 92) {\n            goto st59;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st57;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst17: {  /* DFA node {65,16,1} 17 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_2;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 1;\n    if (c != -1) {\n        if (c == 34) {\n            goto st58;\n        }\n        if (c == 92) {\n            goto st59;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st57;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst18: {  /* DFA node {78,1} 18 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st18_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 1 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st19;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st20;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 1 to row 2 */\n        /* transfer caps from row 1 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st21;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st22;\n        break;\n        }\n    case 92: {\n        goto st23;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st24;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st25;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st26;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st18;\n    }  /* end state */\n\n    goto st18_error;\n\nst19: {  /* DFA node {78,59,1} 19 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st19_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 3 */\n        /* transfer caps from row 2 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st63;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st64;\n        break;\n        }\n    case 92: {\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st66;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st60;\n    }  /* end state */\n\n    goto st19_error;\n\nst20: {  /* DFA node {80,72,1} 20 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_14;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 7;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst21: {  /* DFA node {78,30,50,1} 21 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st21_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st19;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st20;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 4 */\n        /* transfer caps from row 3 to row 5 */\n        /* capture stores */\n        goto st70;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st22;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 1 */\n        goto st23;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st24;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st25;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st26;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 1 */\n    goto st18;\n    }  /* end state */\n\n    goto st21_error;\n\nst22: {  /* DFA node {78,21,1} 22 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st22_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st19;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st20;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st21;\n        break;\n        }\n    case 61: {\n        goto st71;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st72;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st23;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st24;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st25;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st26;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st18;\n    }  /* end state */\n\n    goto st22_error;\n\nst23: {  /* DFA node {75,1} 23 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st23_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 1 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st74;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st75;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 1 to row 2 */\n        /* transfer caps from row 1 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st76;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st77;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st78;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st79;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st80;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c == 92)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st73;\n    }  /* end state */\n\n    goto st23_error;\n\nst24: {  /* DFA node {78,41,1} 24 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st24_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st19;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st20;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st21;\n        break;\n        }\n    case 61: {\n        goto st81;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st22;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st23;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st82;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st25;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st26;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st18;\n    }  /* end state */\n\n    goto st24_error;\n\nst25: {  /* DFA node {78,11,1} 25 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_0;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 0;\n    if (c != -1) {\n        if (c == 39) {\n            goto st84;\n        }\n        if (c == 92) {\n            goto st85;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st83;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst26: {  /* DFA node {78,16,1} 26 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_2;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 1;\n    if (c != -1) {\n        if (c == 39) {\n            goto st84;\n        }\n        if (c == 92) {\n            goto st85;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st83;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst27: {  /* DFA node {31,51,30,50,1} 27 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 91) {\n            goto st88;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 90)\n            || (c >= 92 && c <= 255))\n        {\n            /* transfer caps from row 1 to row 0 */\n            caps0_10 = caps1_10;\n            goto st87;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst28: {  /* DFA node {23,1} 28 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st28_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 34: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_12 = i - 1;\n        goto st2;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_14 = i - 1;\n        goto st3;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 1 to row 0 */\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps0_6 = i - 1;\n        caps1_10 = i - 1;\n        goto st4;\n        break;\n        }\n    case 61: {\n        goto st28;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st29;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_8 = i - 1;\n        goto st6;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_0 = i - 1;\n        goto st7;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_2 = i - 1;\n        goto st8;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c == 92)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 1 to row 0 */\n    goto st1;\n    }  /* end state */\n\n    goto st28_error;\n\nst29: {  /* DFA node {25,21,1} 29 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_4;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 2;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst30: {  /* DFA node {43,1} 30 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st30_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 34: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_12 = i - 1;\n        goto st2;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_14 = i - 1;\n        goto st3;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 1 to row 0 */\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps0_6 = i - 1;\n        caps1_10 = i - 1;\n        goto st4;\n        break;\n        }\n    case 61: {\n        goto st30;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_4 = i - 1;\n        goto st5;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st31;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_0 = i - 1;\n        goto st7;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 1 to row 0 */\n        /* capture stores */\n        caps0_2 = i - 1;\n        goto st8;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c == 92)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 1 to row 0 */\n    goto st1;\n    }  /* end state */\n\n    goto st30_error;\n\nst31: {  /* DFA node {45,41,1} 31 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_8;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 4;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst35: {  /* DFA node {65,78,1} 35 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st35_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 3 */\n        /* transfer caps from row 2 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st38;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st39;\n        break;\n        }\n    case 92: {\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st41;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st35;\n    }  /* end state */\n\n    goto st35_error;\n\nst36: {  /* DFA node {67,78,59,1} 36 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_12;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 6;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst37: {  /* DFA node {65,80,72,1} 37 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_14;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 7;\n    if (c != -1) {\n        if (c == 34) {\n            goto st58;\n        }\n        if (c == 92) {\n            goto st59;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st57;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst38: {  /* DFA node {65,78,30,50,1} 38 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st38_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 4 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 4 to row 5 */\n        /* transfer caps from row 4 to row 6 */\n        /* capture stores */\n        goto st91;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st39;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 4 to row 2 */\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st41;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 4 to row 2 */\n    goto st35;\n    }  /* end state */\n\n    goto st38_error;\n\nst39: {  /* DFA node {65,78,21,1} 39 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st39_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st38;\n        break;\n        }\n    case 61: {\n        goto st92;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        goto st93;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st41;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st35;\n    }  /* end state */\n\n    goto st39_error;\n\nst40: {  /* DFA node {62,75,1} 40 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st40_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st95;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st96;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 3 */\n        /* transfer caps from row 2 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st97;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st98;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st99;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st100;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st101;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c == 92)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st94;\n    }  /* end state */\n\n    goto st40_error;\n\nst41: {  /* DFA node {65,78,41,1} 41 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st41_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st38;\n        break;\n        }\n    case 61: {\n        goto st102;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st39;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        goto st103;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st35;\n    }  /* end state */\n\n    goto st41_error;\n\nst42: {  /* DFA node {65,78,11,1} 42 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_0;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 0;\n    if (c != -1) {\n        if (c == 34) {\n            goto st105;\n        }\n        if (c == 39) {\n            goto st106;\n        }\n        if (c == 92) {\n            goto st107;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st104;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst43: {  /* DFA node {65,78,16,1} 43 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_2;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 1;\n    if (c != -1) {\n        if (c == 34) {\n            goto st105;\n        }\n        if (c == 39) {\n            goto st106;\n        }\n        if (c == 92) {\n            goto st107;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st104;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst44: {  /* DFA node {65,31,51,30,50,1} 44 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st109;\n        }\n        if (c == 91) {\n            goto st110;\n        }\n        if (c == 92) {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st111;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 90)\n            || (c >= 93 && c <= 255))\n        {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st108;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst45: {  /* DFA node {65,23,1} 45 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st45_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st10;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st11;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st12;\n        break;\n        }\n    case 61: {\n        goto st45;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st46;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st14;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st15;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st16;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st17;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st9;\n    }  /* end state */\n\n    goto st45_error;\n\nst46: {  /* DFA node {65,25,21,1} 46 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_4;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 2;\n    if (c != -1) {\n        if (c == 34) {\n            goto st58;\n        }\n        if (c == 92) {\n            goto st59;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st57;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst47: {  /* DFA node {63,1} 47 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st47_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 1 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st10;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st11;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 1 to row 2 */\n        /* transfer caps from row 1 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st12;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st13;\n        break;\n        }\n    case 92: {\n        goto st14;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st15;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st16;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st17;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st9;\n    }  /* end state */\n\n    goto st47_error;\n\nst48: {  /* DFA node {63,59,1} 48 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st48_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st10;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st11;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st12;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st13;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st14;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st15;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st16;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st17;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st9;\n    }  /* end state */\n\n    goto st48_error;\n\nst49: {  /* DFA node {63,72,1} 49 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st49_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 3 */\n        /* transfer caps from row 2 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st38;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st39;\n        break;\n        }\n    case 92: {\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st41;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st35;\n    }  /* end state */\n\n    goto st49_error;\n\nst50: {  /* DFA node {63,30,50,1} 50 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st50_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st10;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st11;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 4 */\n        /* transfer caps from row 3 to row 5 */\n        /* capture stores */\n        goto st44;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st13;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 1 */\n        goto st14;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st15;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st16;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st17;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 1 */\n    goto st9;\n    }  /* end state */\n\n    goto st50_error;\n\nst51: {  /* DFA node {63,21,1} 51 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st51_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st10;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st11;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st12;\n        break;\n        }\n    case 61: {\n        goto st45;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st46;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st14;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st15;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st16;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st17;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st9;\n    }  /* end state */\n\n    goto st51_error;\n\nst52: {  /* DFA node {63,41,1} 52 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st52_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st10;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st11;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st12;\n        break;\n        }\n    case 61: {\n        goto st55;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st13;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st14;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st56;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st16;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st17;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st9;\n    }  /* end state */\n\n    goto st52_error;\n\nst53: {  /* DFA node {63,11,1} 53 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_0;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 0;\n    if (c != -1) {\n        if (c == 34) {\n            goto st58;\n        }\n        if (c == 92) {\n            goto st59;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st57;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst54: {  /* DFA node {63,16,1} 54 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_2;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 1;\n    if (c != -1) {\n        if (c == 34) {\n            goto st58;\n        }\n        if (c == 92) {\n            goto st59;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st57;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst55: {  /* DFA node {65,43,1} 55 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st55_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st10;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st11;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st12;\n        break;\n        }\n    case 61: {\n        goto st55;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st13;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st14;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st56;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st16;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st17;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st9;\n    }  /* end state */\n\n    goto st55_error;\n\nst56: {  /* DFA node {65,45,41,1} 56 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_8;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 4;\n    if (c != -1) {\n        if (c == 34) {\n            goto st58;\n        }\n        if (c == 92) {\n            goto st59;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st57;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst57: {  /* DFA node {65} 57 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st57_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 34: {\n        goto st58;\n        break;\n        }\n    case 92: {\n        goto st59;\n        break;\n        }\n    default:\n        break;\n    }\n    if ((c >= 0 && c <= 9)\n        || (c >= 11 && c <= 33)\n        || (c >= 35 && c <= 91)\n        || (c >= 93 && c <= 255))\n    {\n        goto st57;\n    }\n    }  /* end state */\n\n    goto st57_error;\n\nst58: {  /* DFA node {67} 58 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_12;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 6;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst59: {  /* DFA node {62} 59 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st59_error;\n    }\n\n    c = s[i];\n    i++;\n    if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {\n        goto st112;\n    }\n    }  /* end state */\n\n    goto st59_error;\n\nst60: {  /* DFA node {78,65,1} 60 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st60_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 3 */\n        /* transfer caps from row 2 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st63;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st64;\n        break;\n        }\n    case 92: {\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st66;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st60;\n    }  /* end state */\n\n    goto st60_error;\n\nst61: {  /* DFA node {78,67,59,1} 61 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_12;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 6;\n    if (c != -1) {\n        if (c == 39) {\n            goto st84;\n        }\n        if (c == 92) {\n            goto st85;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st83;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst62: {  /* DFA node {80,65,72,1} 62 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_14;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 7;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst63: {  /* DFA node {78,65,30,50,1} 63 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st63_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 4 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 4 to row 5 */\n        /* transfer caps from row 4 to row 6 */\n        /* capture stores */\n        goto st113;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st64;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 4 to row 2 */\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st66;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 4 to row 2 */\n    goto st60;\n    }  /* end state */\n\n    goto st63_error;\n\nst64: {  /* DFA node {78,65,21,1} 64 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st64_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st63;\n        break;\n        }\n    case 61: {\n        goto st114;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        goto st115;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st66;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st60;\n    }  /* end state */\n\n    goto st64_error;\n\nst65: {  /* DFA node {75,62,1} 65 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st65_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st117;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st118;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 3 */\n        /* transfer caps from row 2 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st119;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st120;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st121;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st122;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st123;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c == 92)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st116;\n    }  /* end state */\n\n    goto st65_error;\n\nst66: {  /* DFA node {78,65,41,1} 66 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st66_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st63;\n        break;\n        }\n    case 61: {\n        goto st124;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st64;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        goto st125;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st60;\n    }  /* end state */\n\n    goto st66_error;\n\nst67: {  /* DFA node {78,65,11,1} 67 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_0;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 0;\n    if (c != -1) {\n        if (c == 34) {\n            goto st127;\n        }\n        if (c == 39) {\n            goto st128;\n        }\n        if (c == 92) {\n            goto st129;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st126;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst68: {  /* DFA node {78,65,16,1} 68 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_2;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 1;\n    if (c != -1) {\n        if (c == 34) {\n            goto st127;\n        }\n        if (c == 39) {\n            goto st128;\n        }\n        if (c == 92) {\n            goto st129;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st126;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst70: {  /* DFA node {78,31,51,30,50,1} 70 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 39) {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st131;\n        }\n        if (c == 91) {\n            goto st132;\n        }\n        if (c == 92) {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st133;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 90)\n            || (c >= 93 && c <= 255))\n        {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st130;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst71: {  /* DFA node {78,23,1} 71 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st71_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st19;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st20;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st21;\n        break;\n        }\n    case 61: {\n        goto st71;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st72;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st23;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st24;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st25;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st26;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st18;\n    }  /* end state */\n\n    goto st71_error;\n\nst72: {  /* DFA node {78,25,21,1} 72 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_4;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 2;\n    if (c != -1) {\n        if (c == 39) {\n            goto st84;\n        }\n        if (c == 92) {\n            goto st85;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st83;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst73: {  /* DFA node {76,1} 73 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st73_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 1 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st19;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st20;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 1 to row 2 */\n        /* transfer caps from row 1 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st21;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st22;\n        break;\n        }\n    case 92: {\n        goto st23;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st24;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st25;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 1 to row 2 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st26;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st18;\n    }  /* end state */\n\n    goto st73_error;\n\nst74: {  /* DFA node {76,59,1} 74 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st74_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 3 */\n        /* transfer caps from row 2 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st63;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st64;\n        break;\n        }\n    case 92: {\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st66;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st60;\n    }  /* end state */\n\n    goto st74_error;\n\nst75: {  /* DFA node {76,72,1} 75 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st75_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st19;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st20;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st21;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st22;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st23;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st24;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st25;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st26;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st18;\n    }  /* end state */\n\n    goto st75_error;\n\nst76: {  /* DFA node {76,30,50,1} 76 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st76_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st19;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st20;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 4 */\n        /* transfer caps from row 3 to row 5 */\n        /* capture stores */\n        goto st70;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st22;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 1 */\n        goto st23;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st24;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st25;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 1 */\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st26;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 1 */\n    goto st18;\n    }  /* end state */\n\n    goto st76_error;\n\nst77: {  /* DFA node {76,21,1} 77 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st77_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st19;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st20;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st21;\n        break;\n        }\n    case 61: {\n        goto st71;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st72;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st23;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_8 = i - 1;\n        goto st24;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st25;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st26;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st18;\n    }  /* end state */\n\n    goto st77_error;\n\nst78: {  /* DFA node {76,41,1} 78 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st78_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st19;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st20;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st21;\n        break;\n        }\n    case 61: {\n        goto st81;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st22;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st23;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st82;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st25;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st26;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st18;\n    }  /* end state */\n\n    goto st78_error;\n\nst79: {  /* DFA node {76,11,1} 79 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_0;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 0;\n    if (c != -1) {\n        if (c == 39) {\n            goto st84;\n        }\n        if (c == 92) {\n            goto st85;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st83;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst80: {  /* DFA node {76,16,1} 80 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_2;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 1;\n    if (c != -1) {\n        if (c == 39) {\n            goto st84;\n        }\n        if (c == 92) {\n            goto st85;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st83;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst81: {  /* DFA node {78,43,1} 81 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st81_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_12 = i - 1;\n        goto st19;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_14 = i - 1;\n        goto st20;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 1 */\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps1_6 = i - 1;\n        caps2_10 = i - 1;\n        goto st21;\n        break;\n        }\n    case 61: {\n        goto st81;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_4 = i - 1;\n        goto st22;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 2 to row 1 */\n        goto st23;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st82;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_0 = i - 1;\n        goto st25;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 1 */\n        /* capture stores */\n        caps1_2 = i - 1;\n        goto st26;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 2 to row 1 */\n    goto st18;\n    }  /* end state */\n\n    goto st81_error;\n\nst82: {  /* DFA node {78,45,41,1} 82 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_8;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 4;\n    if (c != -1) {\n        if (c == 39) {\n            goto st84;\n        }\n        if (c == 92) {\n            goto st85;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st83;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst83: {  /* DFA node {78} 83 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st83_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 39: {\n        goto st84;\n        break;\n        }\n    case 92: {\n        goto st85;\n        break;\n        }\n    default:\n        break;\n    }\n    if ((c >= 0 && c <= 9)\n        || (c >= 11 && c <= 38)\n        || (c >= 40 && c <= 91)\n        || (c >= 93 && c <= 255))\n    {\n        goto st83;\n    }\n    }  /* end state */\n\n    goto st83_error;\n\nst84: {  /* DFA node {80} 84 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_14;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 7;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst85: {  /* DFA node {75} 85 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st85_error;\n    }\n\n    c = s[i];\n    i++;\n    if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {\n        goto st134;\n    }\n    }  /* end state */\n\n    goto st85_error;\n\nst87: {  /* DFA node {53} 87 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {\n            goto st87;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst88: {  /* DFA node {32,53} 88 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 61) {\n            goto st135;\n        }\n        if (c == 91) {\n            goto st136;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 60)\n            || (c >= 62 && c <= 90)\n            || (c >= 92 && c <= 255))\n        {\n            /* transfer caps from row 1 to row 0 */\n            caps0_10 = caps1_10;\n            goto st87;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst91: {  /* DFA node {65,78,31,51,30,50,1} 91 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 3 to matched */\n    matched_0 = caps3_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st138;\n        }\n        if (c == 39) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st139;\n        }\n        if (c == 91) {\n            goto st140;\n        }\n        if (c == 92) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st141;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 90)\n            || (c >= 93 && c <= 255))\n        {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st137;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst92: {  /* DFA node {65,78,23,1} 92 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st92_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st38;\n        break;\n        }\n    case 61: {\n        goto st92;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        goto st93;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st41;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st35;\n    }  /* end state */\n\n    goto st92_error;\n\nst93: {  /* DFA node {65,78,25,21,1} 93 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_4;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 2;\n    if (c != -1) {\n        if (c == 34) {\n            goto st105;\n        }\n        if (c == 39) {\n            goto st106;\n        }\n        if (c == 92) {\n            goto st107;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st104;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst94: {  /* DFA node {63,76,1} 94 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st94_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 3 */\n        /* transfer caps from row 2 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st38;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st39;\n        break;\n        }\n    case 92: {\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st41;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st35;\n    }  /* end state */\n\n    goto st94_error;\n\nst95: {  /* DFA node {63,76,59,1} 95 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st95_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st38;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st39;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st41;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st35;\n    }  /* end state */\n\n    goto st95_error;\n\nst96: {  /* DFA node {63,76,72,1} 96 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st96_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st38;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st39;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st41;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st35;\n    }  /* end state */\n\n    goto st96_error;\n\nst97: {  /* DFA node {63,76,30,50,1} 97 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st97_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 4 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 4 to row 5 */\n        /* transfer caps from row 4 to row 6 */\n        /* capture stores */\n        goto st91;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st39;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 4 to row 2 */\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st41;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 4 to row 2 */\n    goto st35;\n    }  /* end state */\n\n    goto st97_error;\n\nst98: {  /* DFA node {63,76,21,1} 98 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st98_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st38;\n        break;\n        }\n    case 61: {\n        goto st92;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        goto st93;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st41;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st35;\n    }  /* end state */\n\n    goto st98_error;\n\nst99: {  /* DFA node {63,76,41,1} 99 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st99_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st38;\n        break;\n        }\n    case 61: {\n        goto st102;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st39;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        goto st103;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st35;\n    }  /* end state */\n\n    goto st99_error;\n\nst100: {  /* DFA node {63,76,11,1} 100 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_0;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 0;\n    if (c != -1) {\n        if (c == 34) {\n            goto st105;\n        }\n        if (c == 39) {\n            goto st106;\n        }\n        if (c == 92) {\n            goto st107;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st104;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst101: {  /* DFA node {63,76,16,1} 101 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_2;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 1;\n    if (c != -1) {\n        if (c == 34) {\n            goto st105;\n        }\n        if (c == 39) {\n            goto st106;\n        }\n        if (c == 92) {\n            goto st107;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st104;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst102: {  /* DFA node {65,78,43,1} 102 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st102_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st36;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st37;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st38;\n        break;\n        }\n    case 61: {\n        goto st102;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st39;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st40;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        goto st103;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st42;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st43;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st35;\n    }  /* end state */\n\n    goto st102_error;\n\nst103: {  /* DFA node {65,78,45,41,1} 103 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_8;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 4;\n    if (c != -1) {\n        if (c == 34) {\n            goto st105;\n        }\n        if (c == 39) {\n            goto st106;\n        }\n        if (c == 92) {\n            goto st107;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st104;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst104: {  /* DFA node {65,78} 104 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st104_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 34: {\n        goto st105;\n        break;\n        }\n    case 39: {\n        goto st106;\n        break;\n        }\n    case 92: {\n        goto st107;\n        break;\n        }\n    default:\n        break;\n    }\n    if ((c >= 0 && c <= 9)\n        || (c >= 11 && c <= 33)\n        || (c >= 35 && c <= 38)\n        || (c >= 40 && c <= 91)\n        || (c >= 93 && c <= 255))\n    {\n        goto st104;\n    }\n    }  /* end state */\n\n    goto st104_error;\n\nst105: {  /* DFA node {67,78} 105 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_12;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 6;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst106: {  /* DFA node {65,80} 106 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_14;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 7;\n    if (c != -1) {\n        if (c == 34) {\n            goto st58;\n        }\n        if (c == 92) {\n            goto st59;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st57;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst107: {  /* DFA node {62,75} 107 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st107_error;\n    }\n\n    c = s[i];\n    i++;\n    if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {\n        goto st142;\n    }\n    }  /* end state */\n\n    goto st107_error;\n\nst108: {  /* DFA node {65,53} 108 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            goto st109;\n        }\n        if (c == 92) {\n            goto st111;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st108;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst109: {  /* DFA node {67,53} 109 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_12;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 6;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst110: {  /* DFA node {65,32,53} 110 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st109;\n        }\n        if (c == 61) {\n            goto st143;\n        }\n        if (c == 91) {\n            goto st144;\n        }\n        if (c == 92) {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st111;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 60)\n            || (c >= 62 && c <= 90)\n            || (c >= 93 && c <= 255))\n        {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st108;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst111: {  /* DFA node {62,53} 111 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {\n            goto st145;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst112: {  /* DFA node {63} 112 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st112_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 34: {\n        goto st58;\n        break;\n        }\n    case 92: {\n        goto st59;\n        break;\n        }\n    default:\n        break;\n    }\n    if ((c >= 0 && c <= 9)\n        || (c >= 11 && c <= 33)\n        || (c >= 35 && c <= 91)\n        || (c >= 93 && c <= 255))\n    {\n        goto st57;\n    }\n    }  /* end state */\n\n    goto st112_error;\n\nst113: {  /* DFA node {78,65,31,51,30,50,1} 113 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 3 to matched */\n    matched_0 = caps3_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st147;\n        }\n        if (c == 39) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st148;\n        }\n        if (c == 91) {\n            goto st149;\n        }\n        if (c == 92) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st150;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 90)\n            || (c >= 93 && c <= 255))\n        {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st146;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst114: {  /* DFA node {78,65,23,1} 114 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st114_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st63;\n        break;\n        }\n    case 61: {\n        goto st114;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        goto st115;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st66;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st60;\n    }  /* end state */\n\n    goto st114_error;\n\nst115: {  /* DFA node {78,65,25,21,1} 115 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_4;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 2;\n    if (c != -1) {\n        if (c == 34) {\n            goto st127;\n        }\n        if (c == 39) {\n            goto st128;\n        }\n        if (c == 92) {\n            goto st129;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st126;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst116: {  /* DFA node {76,63,1} 116 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st116_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 2 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 2 to row 3 */\n        /* transfer caps from row 2 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st63;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st64;\n        break;\n        }\n    case 92: {\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st66;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 2 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    goto st60;\n    }  /* end state */\n\n    goto st116_error;\n\nst117: {  /* DFA node {76,63,59,1} 117 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st117_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st63;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st64;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st66;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st60;\n    }  /* end state */\n\n    goto st117_error;\n\nst118: {  /* DFA node {76,63,72,1} 118 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st118_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st63;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st64;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st66;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st60;\n    }  /* end state */\n\n    goto st118_error;\n\nst119: {  /* DFA node {76,63,30,50,1} 119 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st119_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 4 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 4 to row 5 */\n        /* transfer caps from row 4 to row 6 */\n        /* capture stores */\n        goto st113;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st64;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 4 to row 2 */\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st66;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 4 to row 2 */\n        /* transfer caps from row 4 to row 3 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 4 to row 2 */\n    goto st60;\n    }  /* end state */\n\n    goto st119_error;\n\nst120: {  /* DFA node {76,63,21,1} 120 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st120_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st63;\n        break;\n        }\n    case 61: {\n        goto st114;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        goto st115;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_8 = i - 1;\n        goto st66;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st60;\n    }  /* end state */\n\n    goto st120_error;\n\nst121: {  /* DFA node {76,63,41,1} 121 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st121_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st63;\n        break;\n        }\n    case 61: {\n        goto st124;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st64;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        goto st125;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st60;\n    }  /* end state */\n\n    goto st121_error;\n\nst122: {  /* DFA node {76,63,11,1} 122 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_0;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 0;\n    if (c != -1) {\n        if (c == 34) {\n            goto st127;\n        }\n        if (c == 39) {\n            goto st128;\n        }\n        if (c == 92) {\n            goto st129;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st126;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst123: {  /* DFA node {76,63,16,1} 123 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_2;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 1;\n    if (c != -1) {\n        if (c == 34) {\n            goto st127;\n        }\n        if (c == 39) {\n            goto st128;\n        }\n        if (c == 92) {\n            goto st129;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st126;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst124: {  /* DFA node {78,65,43,1} 124 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st124_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 10: {\n        /* transfer caps from row 3 to row 0 */\n        goto st1;\n        break;\n        }\n    case 34: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st61;\n        break;\n        }\n    case 39: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        goto st62;\n        break;\n        }\n    case 45: {\n        /* transfer caps from row 3 to row 2 */\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        caps2_6 = i - 1;\n        caps3_10 = i - 1;\n        goto st63;\n        break;\n        }\n    case 61: {\n        goto st124;\n        break;\n        }\n    case 91: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_4 = i - 1;\n        goto st64;\n        break;\n        }\n    case 92: {\n        /* transfer caps from row 3 to row 2 */\n        goto st65;\n        break;\n        }\n    case 93: {\n        /* transfer caps from row 3 to row 4 */\n        /* capture stores */\n        goto st125;\n        break;\n        }\n    case 123: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_0 = i - 1;\n        goto st67;\n        break;\n        }\n    case 125: {\n        /* transfer caps from row 3 to row 2 */\n        /* capture stores */\n        caps2_2 = i - 1;\n        goto st68;\n        break;\n        }\n    default:\n        break;\n    }\n    /* (c >= 0 && c <= 9)\n     * || (c >= 11 && c <= 33)\n     * || (c >= 35 && c <= 38)\n     * || (c >= 40 && c <= 44)\n     * || (c >= 46 && c <= 60)\n     * || (c >= 62 && c <= 90)\n     * || (c >= 94 && c <= 122)\n     * || (c == 124)\n     * || (c >= 126 && c <= 255)\n     */\n    /* transfer caps from row 3 to row 2 */\n    goto st60;\n    }  /* end state */\n\n    goto st124_error;\n\nst125: {  /* DFA node {78,65,45,41,1} 125 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_8;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 4;\n    if (c != -1) {\n        if (c == 34) {\n            goto st127;\n        }\n        if (c == 39) {\n            goto st128;\n        }\n        if (c == 92) {\n            goto st129;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st126;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst126: {  /* DFA node {78,65} 126 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st126_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 34: {\n        goto st127;\n        break;\n        }\n    case 39: {\n        goto st128;\n        break;\n        }\n    case 92: {\n        goto st129;\n        break;\n        }\n    default:\n        break;\n    }\n    if ((c >= 0 && c <= 9)\n        || (c >= 11 && c <= 33)\n        || (c >= 35 && c <= 38)\n        || (c >= 40 && c <= 91)\n        || (c >= 93 && c <= 255))\n    {\n        goto st126;\n    }\n    }  /* end state */\n\n    goto st126_error;\n\nst127: {  /* DFA node {78,67} 127 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_12;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 6;\n    if (c != -1) {\n        if (c == 39) {\n            goto st84;\n        }\n        if (c == 92) {\n            goto st85;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st83;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst128: {  /* DFA node {80,65} 128 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_14;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 7;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst129: {  /* DFA node {75,62} 129 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st129_error;\n    }\n\n    c = s[i];\n    i++;\n    if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {\n        goto st151;\n    }\n    }  /* end state */\n\n    goto st129_error;\n\nst130: {  /* DFA node {78,53} 130 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 39) {\n            goto st131;\n        }\n        if (c == 92) {\n            goto st133;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st130;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst131: {  /* DFA node {80,53} 131 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_14;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 7;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst132: {  /* DFA node {78,32,53} 132 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 39) {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st131;\n        }\n        if (c == 61) {\n            goto st152;\n        }\n        if (c == 91) {\n            goto st153;\n        }\n        if (c == 92) {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st133;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 60)\n            || (c >= 62 && c <= 90)\n            || (c >= 93 && c <= 255))\n        {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st130;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst133: {  /* DFA node {75,53} 133 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {\n            goto st154;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst134: {  /* DFA node {76} 134 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st134_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 39: {\n        goto st84;\n        break;\n        }\n    case 92: {\n        goto st85;\n        break;\n        }\n    default:\n        break;\n    }\n    if ((c >= 0 && c <= 9)\n        || (c >= 11 && c <= 38)\n        || (c >= 40 && c <= 91)\n        || (c >= 93 && c <= 255))\n    {\n        goto st83;\n    }\n    }  /* end state */\n\n    goto st134_error;\n\nst135: {  /* DFA node {34,53} 135 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 61) {\n            goto st135;\n        }\n        if (c == 91) {\n            goto st136;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 60)\n            || (c >= 62 && c <= 90)\n            || (c >= 92 && c <= 255))\n        {\n            /* transfer caps from row 1 to row 0 */\n            caps0_10 = caps1_10;\n            goto st87;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst136: {  /* DFA node {36,53} 136 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_6;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 3;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst137: {  /* DFA node {65,78,53} 137 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            goto st138;\n        }\n        if (c == 39) {\n            goto st139;\n        }\n        if (c == 92) {\n            goto st141;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st137;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst138: {  /* DFA node {67,78,53} 138 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_12;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 6;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst139: {  /* DFA node {65,80,53} 139 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_14;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 7;\n    if (c != -1) {\n        if (c == 34) {\n            goto st58;\n        }\n        if (c == 92) {\n            goto st59;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st57;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst140: {  /* DFA node {65,78,32,53} 140 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 3 to matched */\n    matched_0 = caps3_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st138;\n        }\n        if (c == 39) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st139;\n        }\n        if (c == 61) {\n            goto st156;\n        }\n        if (c == 91) {\n            goto st157;\n        }\n        if (c == 92) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st141;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 60)\n            || (c >= 62 && c <= 90)\n            || (c >= 93 && c <= 255))\n        {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st137;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst141: {  /* DFA node {62,75,53} 141 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {\n            goto st158;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst142: {  /* DFA node {63,76} 142 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st142_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 34: {\n        goto st105;\n        break;\n        }\n    case 39: {\n        goto st106;\n        break;\n        }\n    case 92: {\n        goto st107;\n        break;\n        }\n    default:\n        break;\n    }\n    if ((c >= 0 && c <= 9)\n        || (c >= 11 && c <= 33)\n        || (c >= 35 && c <= 38)\n        || (c >= 40 && c <= 91)\n        || (c >= 93 && c <= 255))\n    {\n        goto st104;\n    }\n    }  /* end state */\n\n    goto st142_error;\n\nst143: {  /* DFA node {65,34,53} 143 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st109;\n        }\n        if (c == 61) {\n            goto st143;\n        }\n        if (c == 91) {\n            goto st144;\n        }\n        if (c == 92) {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st111;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 60)\n            || (c >= 62 && c <= 90)\n            || (c >= 93 && c <= 255))\n        {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st108;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst144: {  /* DFA node {65,36,53} 144 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_6;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 3;\n    if (c != -1) {\n        if (c == 34) {\n            goto st58;\n        }\n        if (c == 92) {\n            goto st59;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st57;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst145: {  /* DFA node {63,53} 145 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            goto st109;\n        }\n        if (c == 92) {\n            goto st111;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st108;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst146: {  /* DFA node {78,65,53} 146 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            goto st147;\n        }\n        if (c == 39) {\n            goto st148;\n        }\n        if (c == 92) {\n            goto st150;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st146;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst147: {  /* DFA node {78,67,53} 147 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_12;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 6;\n    if (c != -1) {\n        if (c == 39) {\n            goto st84;\n        }\n        if (c == 92) {\n            goto st85;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st83;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst148: {  /* DFA node {80,65,53} 148 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 0 to matched */\n    matched_0 = caps0_14;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 7;\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst149: {  /* DFA node {78,65,32,53} 149 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 3 to matched */\n    matched_0 = caps3_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st147;\n        }\n        if (c == 39) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st148;\n        }\n        if (c == 61) {\n            goto st159;\n        }\n        if (c == 91) {\n            goto st160;\n        }\n        if (c == 92) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st150;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 60)\n            || (c >= 62 && c <= 90)\n            || (c >= 93 && c <= 255))\n        {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st146;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst150: {  /* DFA node {75,62,53} 150 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {\n            goto st161;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst151: {  /* DFA node {76,63} 151 */\n    if (unlikely(i >= len)) {\n        i++;\n        goto st151_error;\n    }\n\n    c = s[i];\n    i++;\n    switch (c) {\n    case 34: {\n        goto st127;\n        break;\n        }\n    case 39: {\n        goto st128;\n        break;\n        }\n    case 92: {\n        goto st129;\n        break;\n        }\n    default:\n        break;\n    }\n    if ((c >= 0 && c <= 9)\n        || (c >= 11 && c <= 33)\n        || (c >= 35 && c <= 38)\n        || (c >= 40 && c <= 91)\n        || (c >= 93 && c <= 255))\n    {\n        goto st126;\n    }\n    }  /* end state */\n\n    goto st151_error;\n\nst152: {  /* DFA node {78,34,53} 152 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 39) {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st131;\n        }\n        if (c == 61) {\n            goto st152;\n        }\n        if (c == 91) {\n            goto st153;\n        }\n        if (c == 92) {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st133;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 60)\n            || (c >= 62 && c <= 90)\n            || (c >= 93 && c <= 255))\n        {\n            /* transfer caps from row 2 to row 1 */\n            caps1_10 = caps2_10;\n            goto st130;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst153: {  /* DFA node {78,36,53} 153 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_6;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 3;\n    if (c != -1) {\n        if (c == 39) {\n            goto st84;\n        }\n        if (c == 92) {\n            goto st85;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st83;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst154: {  /* DFA node {76,53} 154 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 1 to matched */\n    matched_0 = caps1_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 39) {\n            goto st131;\n        }\n        if (c == 92) {\n            goto st133;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st130;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst156: {  /* DFA node {65,78,34,53} 156 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 3 to matched */\n    matched_0 = caps3_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st138;\n        }\n        if (c == 39) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st139;\n        }\n        if (c == 61) {\n            goto st156;\n        }\n        if (c == 91) {\n            goto st157;\n        }\n        if (c == 92) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st141;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 60)\n            || (c >= 62 && c <= 90)\n            || (c >= 93 && c <= 255))\n        {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st137;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst157: {  /* DFA node {65,78,36,53} 157 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_6;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 3;\n    if (c != -1) {\n        if (c == 34) {\n            goto st105;\n        }\n        if (c == 39) {\n            goto st106;\n        }\n        if (c == 92) {\n            goto st107;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st104;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst158: {  /* DFA node {63,76,53} 158 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            goto st138;\n        }\n        if (c == 39) {\n            goto st139;\n        }\n        if (c == 92) {\n            goto st141;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st137;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst159: {  /* DFA node {78,65,34,53} 159 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 3 to matched */\n    matched_0 = caps3_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st147;\n        }\n        if (c == 39) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st148;\n        }\n        if (c == 61) {\n            goto st159;\n        }\n        if (c == 91) {\n            goto st160;\n        }\n        if (c == 92) {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st150;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 60)\n            || (c >= 62 && c <= 90)\n            || (c >= 93 && c <= 255))\n        {\n            /* transfer caps from row 3 to row 2 */\n            caps2_10 = caps3_10;\n            goto st146;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst160: {  /* DFA node {78,65,36,53} 160 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_6;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 3;\n    if (c != -1) {\n        if (c == 34) {\n            goto st127;\n        }\n        if (c == 39) {\n            goto st128;\n        }\n        if (c == 92) {\n            goto st129;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st126;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst161: {  /* DFA node {76,63,53} 161 */\n    c = i < len ? s[i] : -1;\n    i++;\n    /* transfer caps from row 2 to matched */\n    matched_0 = caps2_10;\n    /* capture stores */\n    matched_1 = i - 1;\n    matched_id = 5;\n    if (c != -1) {\n        if (c == 34) {\n            goto st147;\n        }\n        if (c == 39) {\n            goto st148;\n        }\n        if (c == 92) {\n            goto st150;\n        }\n        if ((c >= 0 && c <= 9)\n            || (c >= 11 && c <= 33)\n            || (c >= 35 && c <= 38)\n            || (c >= 40 && c <= 91)\n            || (c >= 93 && c <= 255))\n        {\n            goto st146;\n        }\n    }\n    }  /* end state */\n\n    ovec[0] = matched_0;\n    ovec[1] = matched_1;\n    return matched_id;  /* fallback */\n\nst0_error:\nst1_error:\nst2_error:\nst3_error:\nst4_error:\nst5_error:\nst6_error:\nst9_error:\nst11_error:\nst12_error:\nst13_error:\nst14_error:\nst15_error:\nst18_error:\nst19_error:\nst21_error:\nst22_error:\nst23_error:\nst24_error:\nst28_error:\nst30_error:\nst35_error:\nst38_error:\nst39_error:\nst40_error:\nst41_error:\nst45_error:\nst47_error:\nst48_error:\nst49_error:\nst50_error:\nst51_error:\nst52_error:\nst55_error:\nst57_error:\nst59_error:\nst60_error:\nst63_error:\nst64_error:\nst65_error:\nst66_error:\nst71_error:\nst73_error:\nst74_error:\nst75_error:\nst76_error:\nst77_error:\nst78_error:\nst81_error:\nst83_error:\nst85_error:\nst92_error:\nst94_error:\nst95_error:\nst96_error:\nst97_error:\nst98_error:\nst99_error:\nst102_error:\nst104_error:\nst107_error:\nst112_error:\nst114_error:\nst116_error:\nst117_error:\nst118_error:\nst119_error:\nst120_error:\nst121_error:\nst124_error:\nst126_error:\nst129_error:\nst134_error:\nst142_error:\nst151_error:\n\n    if (matched_0 != -1) {\n        ovec[0] = matched_0;\n        ovec[1] = matched_1;\n        return matched_id;  /* fallback */\n    }\n    return NO_MATCH;\n}\n"
  },
  {
    "path": "src/ngx_http_lua_lex.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_LEX_H_INCLUDED_\n#define _NGX_HTTP_LUA_LEX_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nint ngx_http_lua_lex(const u_char *const s, size_t len, int *const ovec);\n\n\n#endif\n"
  },
  {
    "path": "src/ngx_http_lua_log.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_log.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_log_ringbuf.h\"\n#include \"ngx_http_lua_output.h\"\n\n\nstatic int ngx_http_lua_print(lua_State *L);\nstatic int ngx_http_lua_ngx_log(lua_State *L);\nstatic int log_wrapper(ngx_log_t *log, const char *ident,\n    ngx_uint_t level, lua_State *L);\nstatic void ngx_http_lua_inject_log_consts(lua_State *L);\n\n\n/**\n * Wrapper of nginx log functionality. Take a log level param and varargs of\n * log message params.\n *\n * @param L Lua state pointer\n * @retval always 0 (don't return values to Lua)\n * */\nint\nngx_http_lua_ngx_log(lua_State *L)\n{\n    ngx_log_t                   *log;\n    ngx_http_request_t          *r;\n    const char                  *msg;\n    int                          level;\n\n    r = ngx_http_lua_get_req(L);\n\n    if (r && r->connection && r->connection->log) {\n        log = r->connection->log;\n\n    } else {\n        log = ngx_cycle->log;\n    }\n\n    level = luaL_checkint(L, 1);\n    if (level < NGX_LOG_STDERR || level > NGX_LOG_DEBUG) {\n        msg = lua_pushfstring(L, \"bad log level: %d\", level);\n        return luaL_argerror(L, 1, msg);\n    }\n\n    /* remove log-level param from stack */\n    lua_remove(L, 1);\n\n    return log_wrapper(log, \"[lua] \", (ngx_uint_t) level, L);\n}\n\n\n/**\n * Override Lua print function, output message to nginx error logs. Equal to\n * ngx.log(ngx.NOTICE, ...).\n *\n * @param L Lua state pointer\n * @retval always 0 (don't return values to Lua)\n * */\nint\nngx_http_lua_print(lua_State *L)\n{\n    ngx_log_t                   *log;\n    ngx_http_request_t          *r;\n\n    r = ngx_http_lua_get_req(L);\n\n    if (r && r->connection && r->connection->log) {\n        log = r->connection->log;\n\n    } else {\n        log = ngx_cycle->log;\n    }\n\n    return log_wrapper(log, \"[lua] \", NGX_LOG_NOTICE, L);\n}\n\n\nstatic int\nlog_wrapper(ngx_log_t *log, const char *ident, ngx_uint_t level,\n    lua_State *L)\n{\n    u_char              *buf;\n    u_char              *p, *q;\n    ngx_str_t            name;\n    int                  nargs, i;\n    size_t               size, len;\n    size_t               src_len = 0;\n    int                  type;\n    const char          *msg;\n    lua_Debug            ar;\n\n    if (level > log->log_level) {\n        return 0;\n    }\n\n#if 1\n    /* add debug info */\n\n    lua_getstack(L, 1, &ar);\n    lua_getinfo(L, \"Snl\", &ar);\n\n    /* get the basename of the Lua source file path, stored in q */\n    name.data = (u_char *) ar.short_src;\n    if (name.data == NULL) {\n        name.len = 0;\n\n    } else {\n        p = name.data;\n        while (*p != '\\0') {\n            if (*p == '/' || *p == '\\\\') {\n                name.data = p + 1;\n            }\n\n            p++;\n        }\n\n        name.len = p - name.data;\n    }\n\n#endif\n\n    nargs = lua_gettop(L);\n\n    size = name.len + NGX_INT_T_LEN + sizeof(\":: \") - 1;\n\n    if (*ar.namewhat != '\\0' && *ar.what == 'L') {\n        src_len = ngx_strlen(ar.name);\n        size += src_len + sizeof(\"(): \") - 1;\n    }\n\n    for (i = 1; i <= nargs; i++) {\n        type = lua_type(L, i);\n        switch (type) {\n            case LUA_TNUMBER:\n                size += ngx_http_lua_get_num_len(L, i);\n                break;\n\n            case LUA_TSTRING:\n                lua_tolstring(L, i, &len);\n                size += len;\n                break;\n\n            case LUA_TNIL:\n                size += sizeof(\"nil\") - 1;\n                break;\n\n            case LUA_TBOOLEAN:\n                if (lua_toboolean(L, i)) {\n                    size += sizeof(\"true\") - 1;\n\n                } else {\n                    size += sizeof(\"false\") - 1;\n                }\n\n                break;\n\n            case LUA_TTABLE:\n                if (!luaL_callmeta(L, i, \"__tostring\")) {\n                    return luaL_argerror(L, i, \"expected table to have \"\n                                         \"__tostring metamethod\");\n                }\n\n                lua_tolstring(L, -1, &len);\n                size += len;\n                break;\n\n            case LUA_TLIGHTUSERDATA:\n                if (lua_touserdata(L, i) == NULL) {\n                    size += sizeof(\"null\") - 1;\n                    break;\n                }\n\n                continue;\n\n            default:\n                msg = lua_pushfstring(L, \"string, number, boolean, or nil \"\n                                      \"expected, got %s\",\n                                      lua_typename(L, type));\n                return luaL_argerror(L, i, msg);\n        }\n    }\n\n    buf = lua_newuserdata(L, size);\n\n    p = ngx_copy(buf, name.data, name.len);\n\n    *p++ = ':';\n\n    p = ngx_snprintf(p, NGX_INT_T_LEN, \"%d\",\n                     ar.currentline > 0 ? ar.currentline : ar.linedefined);\n\n    *p++ = ':'; *p++ = ' ';\n\n    if (*ar.namewhat != '\\0' && *ar.what == 'L') {\n        p = ngx_copy(p, ar.name, src_len);\n        *p++ = '(';\n        *p++ = ')';\n        *p++ = ':';\n        *p++ = ' ';\n    }\n\n    for (i = 1; i <= nargs; i++) {\n        type = lua_type(L, i);\n        switch (type) {\n            case LUA_TNUMBER:\n                p = ngx_http_lua_write_num(L, i, p);\n                break;\n\n            case LUA_TSTRING:\n                q = (u_char *) lua_tolstring(L, i, &len);\n                p = ngx_copy(p, q, len);\n                break;\n\n            case LUA_TNIL:\n                *p++ = 'n';\n                *p++ = 'i';\n                *p++ = 'l';\n                break;\n\n            case LUA_TBOOLEAN:\n                if (lua_toboolean(L, i)) {\n                    *p++ = 't';\n                    *p++ = 'r';\n                    *p++ = 'u';\n                    *p++ = 'e';\n\n                } else {\n                    *p++ = 'f';\n                    *p++ = 'a';\n                    *p++ = 'l';\n                    *p++ = 's';\n                    *p++ = 'e';\n                }\n\n                break;\n\n            case LUA_TTABLE:\n                luaL_callmeta(L, i, \"__tostring\");\n                q = (u_char *) lua_tolstring(L, -1, &len);\n                p = ngx_copy(p, q, len);\n                break;\n\n            case LUA_TLIGHTUSERDATA:\n                *p++ = 'n';\n                *p++ = 'u';\n                *p++ = 'l';\n                *p++ = 'l';\n\n                break;\n\n            default:\n                return luaL_error(L, \"impossible to reach here\");\n        }\n    }\n\n    if (p - buf > (off_t) size) {\n        return luaL_error(L, \"buffer error: %d > %d\", (int) (p - buf),\n                          (int) size);\n    }\n\n    ngx_log_error(level, log, 0, \"%s%*s\", ident, (size_t) (p - buf), buf);\n\n    return 0;\n}\n\n\nvoid\nngx_http_lua_inject_log_api(lua_State *L)\n{\n    ngx_http_lua_inject_log_consts(L);\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_log);\n    lua_setfield(L, -2, \"log\");\n\n    lua_pushcfunction(L, ngx_http_lua_print);\n    lua_setglobal(L, \"print\");\n}\n\n\nstatic void\nngx_http_lua_inject_log_consts(lua_State *L)\n{\n    /* {{{ nginx log level constants */\n    lua_pushinteger(L, NGX_LOG_STDERR);\n    lua_setfield(L, -2, \"STDERR\");\n\n    lua_pushinteger(L, NGX_LOG_EMERG);\n    lua_setfield(L, -2, \"EMERG\");\n\n    lua_pushinteger(L, NGX_LOG_ALERT);\n    lua_setfield(L, -2, \"ALERT\");\n\n    lua_pushinteger(L, NGX_LOG_CRIT);\n    lua_setfield(L, -2, \"CRIT\");\n\n    lua_pushinteger(L, NGX_LOG_ERR);\n    lua_setfield(L, -2, \"ERR\");\n\n    lua_pushinteger(L, NGX_LOG_WARN);\n    lua_setfield(L, -2, \"WARN\");\n\n    lua_pushinteger(L, NGX_LOG_NOTICE);\n    lua_setfield(L, -2, \"NOTICE\");\n\n    lua_pushinteger(L, NGX_LOG_INFO);\n    lua_setfield(L, -2, \"INFO\");\n\n    lua_pushinteger(L, NGX_LOG_DEBUG);\n    lua_setfield(L, -2, \"DEBUG\");\n    /* }}} */\n}\n\n\n#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH\nngx_int_t\nngx_http_lua_capture_log_handler(ngx_log_t *log,\n    ngx_uint_t level, u_char *buf, size_t n)\n{\n    ngx_http_lua_log_ringbuf_t  *ringbuf;\n\n    dd(\"enter\");\n\n    ringbuf = (ngx_http_lua_log_ringbuf_t  *)\n                    ngx_cycle->intercept_error_log_data;\n\n    if (level > ringbuf->filter_level) {\n        return NGX_OK;\n    }\n\n    ngx_http_lua_log_ringbuf_write(ringbuf, level, buf, n);\n\n    dd(\"capture log: %s\\n\", buf);\n\n    return NGX_OK;\n}\n#endif\n\n\nint\nngx_http_lua_ffi_errlog_set_filter_level(int level, u_char *err, size_t *errlen)\n{\n#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH\n    ngx_http_lua_log_ringbuf_t     *ringbuf;\n\n    ringbuf = ngx_cycle->intercept_error_log_data;\n\n    if (ringbuf == NULL) {\n        *errlen = ngx_snprintf(err, *errlen,\n                               \"directive \\\"lua_capture_error_log\\\" is not set\")\n                  - err;\n        return NGX_ERROR;\n    }\n\n    if (level > NGX_LOG_DEBUG || level < NGX_LOG_STDERR) {\n        *errlen = ngx_snprintf(err, *errlen, \"bad log level: %d\", level)\n                  - err;\n        return NGX_ERROR;\n    }\n\n    ringbuf->filter_level = level;\n    return NGX_OK;\n#else\n    *errlen = ngx_snprintf(err, *errlen,\n                           \"missing the capture error log patch for nginx\")\n              - err;\n    return NGX_ERROR;\n#endif\n}\n\n\nint\nngx_http_lua_ffi_errlog_get_msg(char **log, int *loglevel, u_char *err,\n    size_t *errlen, double *log_time)\n{\n#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH\n    ngx_uint_t           loglen;\n\n    ngx_http_lua_log_ringbuf_t     *ringbuf;\n\n    ringbuf = ngx_cycle->intercept_error_log_data;\n\n    if (ringbuf == NULL) {\n        *errlen = ngx_snprintf(err, *errlen,\n                               \"directive \\\"lua_capture_error_log\\\" is not set\")\n                  - err;\n        return NGX_ERROR;\n    }\n\n    if (ringbuf->count == 0) {\n        return NGX_DONE;\n    }\n\n    ngx_http_lua_log_ringbuf_read(ringbuf, loglevel, (void **) log, &loglen,\n                                  log_time);\n    return loglen;\n#else\n    *errlen = ngx_snprintf(err, *errlen,\n                           \"missing the capture error log patch for nginx\")\n              - err;\n    return NGX_ERROR;\n#endif\n}\n\n\nint\nngx_http_lua_ffi_errlog_get_sys_filter_level(ngx_http_request_t *r)\n{\n    ngx_log_t                   *log;\n    int                          log_level;\n\n    if (r && r->connection && r->connection->log) {\n        log = r->connection->log;\n\n    } else {\n        log = ngx_cycle->log;\n    }\n\n    log_level = log->log_level;\n    if (log_level == NGX_LOG_DEBUG_ALL) {\n        log_level = NGX_LOG_DEBUG;\n    }\n\n    return log_level;\n}\n\n\nint\nngx_http_lua_ffi_raw_log(ngx_http_request_t *r, int level, u_char *s,\n    size_t s_len)\n{\n    ngx_log_t           *log;\n\n    if (level > NGX_LOG_DEBUG || level < NGX_LOG_STDERR) {\n        return NGX_ERROR;\n    }\n\n    if (r && r->connection && r->connection->log) {\n        log = r->connection->log;\n\n    } else {\n        log = ngx_cycle->log;\n    }\n\n    ngx_log_error((unsigned) level, log, 0, \"%*s\", s_len, s);\n\n    return NGX_OK;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_log.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_LOG_H_INCLUDED_\n#define _NGX_HTTP_LUA_LOG_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_log_api(lua_State *L);\n#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH\nngx_int_t ngx_http_lua_capture_log_handler(ngx_log_t *log,\n    ngx_uint_t level, u_char *buf, size_t n);\n#endif\n\n\n#endif /* _NGX_HTTP_LUA_LOG_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_log_ringbuf.c",
    "content": "\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_common.h\"\n#include \"ngx_http_lua_log_ringbuf.h\"\n\n\ntypedef struct {\n    double      time;\n    unsigned    len;\n    unsigned    log_level;\n} ngx_http_lua_log_ringbuf_header_t;\n\n\nenum {\n    HEADER_LEN = sizeof(ngx_http_lua_log_ringbuf_header_t),\n};\n\n\nstatic void *ngx_http_lua_log_ringbuf_next_header(\n    ngx_http_lua_log_ringbuf_t *rb);\nstatic void ngx_http_lua_log_ringbuf_append(\n    ngx_http_lua_log_ringbuf_t *rb, int log_level, void *buf, int n);\nstatic size_t ngx_http_lua_log_ringbuf_free_spaces(\n    ngx_http_lua_log_ringbuf_t *rb);\n\n\nvoid\nngx_http_lua_log_ringbuf_init(ngx_http_lua_log_ringbuf_t *rb, void *buf,\n    size_t len)\n{\n    rb->data = buf;\n    rb->size = len;\n\n    rb->tail = rb->data;\n    rb->head = rb->data;\n    rb->sentinel = rb->data + rb->size;\n    rb->count = 0;\n    rb->filter_level = NGX_LOG_DEBUG;\n\n    return;\n}\n\n\nvoid\nngx_http_lua_log_ringbuf_reset(ngx_http_lua_log_ringbuf_t *rb)\n{\n    rb->tail = rb->data;\n    rb->head = rb->data;\n    rb->sentinel = rb->data + rb->size;\n    rb->count = 0;\n\n    return;\n}\n\n\n/*\n * get the next data header, it'll skip the useless data space or\n * placehold data\n */\nstatic void *\nngx_http_lua_log_ringbuf_next_header(ngx_http_lua_log_ringbuf_t *rb)\n{\n    /* useless data */\n    if (rb->size - (rb->head - rb->data) < HEADER_LEN)\n    {\n        return rb->data;\n    }\n\n    /* placehold data */\n    if (rb->head >= rb->sentinel) {\n        return rb->data;\n    }\n\n    return rb->head;\n}\n\n\n/* append data to ring buffer directly */\nstatic void\nngx_http_lua_log_ringbuf_append(ngx_http_lua_log_ringbuf_t *rb,\n    int log_level, void *buf, int n)\n{\n    ngx_http_lua_log_ringbuf_header_t        *head;\n    ngx_time_t                               *tp;\n\n    head = (ngx_http_lua_log_ringbuf_header_t *) rb->tail;\n    head->len = n;\n    head->log_level = log_level;\n\n    tp = ngx_timeofday();\n    head->time = tp->sec + tp->msec / 1000.0L;\n\n    rb->tail += HEADER_LEN;\n    ngx_memcpy(rb->tail, buf, n);\n    rb->tail += n;\n    rb->count++;\n\n    if (rb->tail > rb->sentinel) {\n        rb->sentinel = rb->tail;\n    }\n\n    return;\n}\n\n\n/* throw away data at head */\nstatic void\nngx_http_lua_log_ringbuf_throw_away(ngx_http_lua_log_ringbuf_t *rb)\n{\n    ngx_http_lua_log_ringbuf_header_t       *head;\n\n    if (rb->count == 0) {\n        return;\n    }\n\n    head = (ngx_http_lua_log_ringbuf_header_t *) rb->head;\n\n    rb->head += HEADER_LEN + head->len;\n    rb->count--;\n\n    if (rb->count == 0) {\n        ngx_http_lua_log_ringbuf_reset(rb);\n    }\n\n    rb->head = ngx_http_lua_log_ringbuf_next_header(rb);\n\n    return;\n}\n\n\n/* size of free spaces */\nstatic size_t\nngx_http_lua_log_ringbuf_free_spaces(ngx_http_lua_log_ringbuf_t *rb)\n{\n    if (rb->count == 0) {\n        return rb->size;\n    }\n\n    if (rb->tail > rb->head) {\n        return rb->data + rb->size - rb->tail;\n    }\n\n    return rb->head - rb->tail;\n}\n\n\n/*\n * try to write log data to ring buffer, throw away old data\n * if there was not enough free spaces.\n */\nngx_int_t\nngx_http_lua_log_ringbuf_write(ngx_http_lua_log_ringbuf_t *rb, int log_level,\n    void *buf, size_t n)\n{\n    if (n + HEADER_LEN > rb->size) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_lua_log_ringbuf_free_spaces(rb) < n + HEADER_LEN) {\n        /* if the right space is not enough, mark it as placehold data */\n        if ((size_t)(rb->data + rb->size - rb->tail) < n + HEADER_LEN) {\n\n            while (rb->head >= rb->tail && rb->count) {\n                /* head is after tail, so we will throw away all data between\n                 * head and sentinel */\n                ngx_http_lua_log_ringbuf_throw_away(rb);\n            }\n\n            rb->sentinel = rb->tail;\n            rb->tail = rb->data;\n        }\n\n        while (ngx_http_lua_log_ringbuf_free_spaces(rb) < n + HEADER_LEN) {\n            ngx_http_lua_log_ringbuf_throw_away(rb);\n        }\n    }\n\n    ngx_http_lua_log_ringbuf_append(rb, log_level, buf, n);\n\n    return NGX_OK;\n}\n\n\n/* read log from ring buffer, do reset if all of the logs were readed. */\nngx_int_t\nngx_http_lua_log_ringbuf_read(ngx_http_lua_log_ringbuf_t *rb, int *log_level,\n    void **buf, size_t *n, double *log_time)\n{\n    ngx_http_lua_log_ringbuf_header_t       *head;\n\n    if (rb->count == 0) {\n        return NGX_ERROR;\n    }\n\n    head = (ngx_http_lua_log_ringbuf_header_t *) rb->head;\n\n    if (rb->head >= rb->sentinel) {\n        return NGX_ERROR;\n    }\n\n    *log_level = head->log_level;\n    *n = head->len;\n    rb->head += HEADER_LEN;\n    *buf = rb->head;\n    rb->head += head->len;\n\n    if (log_time) {\n        *log_time = head->time;\n    }\n\n    rb->count--;\n\n    if (rb->count == 0) {\n        ngx_http_lua_log_ringbuf_reset(rb);\n    }\n\n    rb->head = ngx_http_lua_log_ringbuf_next_header(rb);\n\n    return NGX_OK;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_log_ringbuf.h",
    "content": "\n#ifndef _NGX_HTTP_LUA_RINGBUF_H_INCLUDED_\n#define _NGX_HTTP_LUA_RINGBUF_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\ntypedef struct {\n    ngx_uint_t   filter_level;\n    char        *tail;              /* writed point */\n    char        *head;              /* readed point */\n    char        *data;              /* buffer */\n    char        *sentinel;\n    size_t       size;              /* buffer total size */\n    size_t       count;             /* count of logs */\n} ngx_http_lua_log_ringbuf_t;\n\n\nvoid ngx_http_lua_log_ringbuf_init(ngx_http_lua_log_ringbuf_t *rb,\n    void *buf, size_t len);\nvoid ngx_http_lua_log_ringbuf_reset(ngx_http_lua_log_ringbuf_t *rb);\nngx_int_t ngx_http_lua_log_ringbuf_read(ngx_http_lua_log_ringbuf_t *rb,\n    int *log_level, void **buf, size_t *n, double *log_time);\nngx_int_t ngx_http_lua_log_ringbuf_write(ngx_http_lua_log_ringbuf_t *rb,\n    int log_level, void *buf, size_t n);\n\n\n#endif /* _NGX_HTTP_LUA_RINGBUF_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_logby.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_directive.h\"\n#include \"ngx_http_lua_logby.h\"\n#include \"ngx_http_lua_exception.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_pcrefix.h\"\n#include \"ngx_http_lua_log.h\"\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_headers.h\"\n#include \"ngx_http_lua_string.h\"\n#include \"ngx_http_lua_misc.h\"\n#include \"ngx_http_lua_consts.h\"\n#include \"ngx_http_lua_shdict.h\"\n#if (NGX_HTTP_LUA_HAVE_MALLOC_TRIM)\n#include <malloc.h>\n#endif\n\n\nstatic ngx_int_t ngx_http_lua_log_by_chunk(lua_State *L, ngx_http_request_t *r);\n\n\nstatic void\nngx_http_lua_log_by_lua_env(lua_State *L, ngx_http_request_t *r)\n{\n    ngx_http_lua_set_req(L, r);\n\n#ifndef OPENRESTY_LUAJIT\n    /**\n     * we want to create empty environment for current script\n     *\n     * newt = {}\n     * newt[\"_G\"] = newt\n     * setmetatable(newt, {__index = _G})\n     *\n     * if a function or symbol is not defined in our env, __index will lookup\n     * in the global env.\n     *\n     * all variables created in the script-env will be thrown away at the end\n     * of the script run.\n     * */\n    ngx_http_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);\n\n    /*  {{{ make new env inheriting main thread's globals table */\n    lua_createtable(L, 0, 1);    /*  the metatable for the new env */\n    ngx_http_lua_get_globals_table(L);\n    lua_setfield(L, -2, \"__index\");\n    lua_setmetatable(L, -2);    /*  setmetatable({}, {__index = _G}) */\n    /*  }}} */\n\n    lua_setfenv(L, -2);    /*  set new running env for the code closure */\n#endif /* OPENRESTY_LUAJIT */\n}\n\n\nngx_int_t\nngx_http_lua_log_handler(ngx_http_request_t *r)\n{\n#if (NGX_HTTP_LUA_HAVE_MALLOC_TRIM)\n    ngx_uint_t                   trim_cycle, trim_nreq;\n    ngx_http_lua_main_conf_t    *lmcf;\n#if (NGX_DEBUG)\n    ngx_int_t                    trim_ret;\n#endif\n#endif\n    ngx_http_lua_loc_conf_t     *llcf;\n    ngx_http_lua_ctx_t          *ctx;\n\n#if (NGX_HTTP_LUA_HAVE_MALLOC_TRIM)\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    trim_cycle = lmcf->malloc_trim_cycle;\n\n    if (trim_cycle > 0) {\n\n        dd(\"cycle: %d\", (int) trim_cycle);\n\n        trim_nreq = ++lmcf->malloc_trim_req_count;\n\n        if (trim_nreq >= trim_cycle) {\n            lmcf->malloc_trim_req_count = 0;\n\n#if (NGX_DEBUG)\n            trim_ret = malloc_trim(1);\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"malloc_trim(1) returned %d\", trim_ret);\n#else\n            (void) malloc_trim(1);\n#endif\n        }\n    }\n#   if (NGX_DEBUG)\n    else {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"malloc_trim() disabled\");\n    }\n#   endif\n#endif\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua log handler, uri:\\\"%V\\\" c:%ud\", &r->uri,\n                   r->main->count);\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->log_handler == NULL) {\n        dd(\"no log handler found\");\n        return NGX_DECLINED;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    dd(\"ctx = %p\", ctx);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_LOG;\n\n    dd(\"calling log handler\");\n    return llcf->log_handler(r);\n}\n\n\nngx_int_t\nngx_http_lua_log_handler_inline(ngx_http_request_t *r)\n{\n    lua_State                   *L;\n    ngx_int_t                    rc;\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    dd(\"log by lua inline\");\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->log_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua inline script (w/ cache) sp = 1 */\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       llcf->log_src.value.data,\n                                       llcf->log_src.value.len,\n                                       &llcf->log_src_ref,\n                                       llcf->log_src_key,\n                                       (const char *) llcf->log_chunkname);\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return ngx_http_lua_log_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_log_handler_file(ngx_http_request_t *r)\n{\n    lua_State                       *L;\n    ngx_int_t                        rc;\n    u_char                          *script_path;\n    ngx_http_lua_loc_conf_t         *llcf;\n    ngx_str_t                        eval_src;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (ngx_http_complex_value(r, &llcf->log_src, &eval_src) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,\n                                           eval_src.len);\n\n    if (script_path == NULL) {\n        return NGX_ERROR;\n    }\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->log_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua script file (w/ cache)        sp = 1 */\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,\n                                     &llcf->log_src_ref,\n                                     llcf->log_src_key);\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return ngx_http_lua_log_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_log_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    ngx_int_t        rc;\n    u_char          *err_msg;\n    size_t           len;\n#if (NGX_PCRE)\n    ngx_pool_t      *old_pool;\n#endif\n\n    /*  set Lua VM panic handler */\n    lua_atpanic(L, ngx_http_lua_atpanic);\n\n    NGX_LUA_EXCEPTION_TRY {\n\n        /* initialize nginx context in Lua VM, code chunk at stack top sp = 1 */\n        ngx_http_lua_log_by_lua_env(L, r);\n\n#if (NGX_PCRE)\n        /* XXX: work-around to nginx regex subsystem */\n        old_pool = ngx_http_lua_pcre_malloc_init(r->pool);\n#endif\n\n        lua_pushcfunction(L, ngx_http_lua_traceback);\n        lua_insert(L, 1);  /* put it under chunk and args */\n\n        /*  protected call user code */\n        rc = lua_pcall(L, 0, 1, 1);\n\n        lua_remove(L, 1);  /* remove traceback function */\n\n#if (NGX_PCRE)\n        /* XXX: work-around to nginx regex subsystem */\n        ngx_http_lua_pcre_malloc_done(old_pool);\n#endif\n\n        if (rc != 0) {\n            /*  error occurred when running loaded code */\n            err_msg = (u_char *) lua_tolstring(L, -1, &len);\n\n            if (err_msg == NULL) {\n                err_msg = (u_char *) \"unknown reason\";\n                len = sizeof(\"unknown reason\") - 1;\n            }\n\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"failed to run log_by_lua*: %*s\", len, err_msg);\n\n            lua_settop(L, 0);    /*  clear remaining elems on stack */\n\n            return NGX_ERROR;\n        }\n\n    } NGX_LUA_EXCEPTION_CATCH {\n\n        dd(\"nginx execution restored\");\n        return NGX_ERROR;\n    }\n\n    /*  clear Lua stack */\n    lua_settop(L, 0);\n\n    return NGX_OK;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_logby.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_LOGBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_LOGBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_log_handler(ngx_http_request_t *r);\nngx_int_t ngx_http_lua_log_handler_inline(ngx_http_request_t *r);\nngx_int_t ngx_http_lua_log_handler_file(ngx_http_request_t *r);\n\n\n#endif /* _NGX_HTTP_LUA_LOGBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_misc.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_misc.h\"\n#include \"ngx_http_lua_util.h\"\n\n\nstatic int ngx_http_lua_ngx_req_is_internal(lua_State *L);\n\n\nvoid\nngx_http_lua_inject_req_misc_api(lua_State *L)\n{\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_is_internal);\n    lua_setfield(L, -2, \"is_internal\");\n}\n\n\nstatic int\nngx_http_lua_ngx_req_is_internal(lua_State *L)\n{\n    ngx_http_request_t  *r;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request object found\");\n    }\n\n    lua_pushboolean(L, r->internal == 1);\n    return 1;\n}\n\n\nint\nngx_http_lua_ffi_get_resp_status(ngx_http_request_t *r)\n{\n    if (r->connection->fd == (ngx_socket_t) -1) {\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    if (r->err_status) {\n        return r->err_status;\n\n    } else if (r->headers_out.status) {\n        return r->headers_out.status;\n\n    } else if (r->http_version == NGX_HTTP_VERSION_9) {\n        return 9;\n\n    } else {\n        return 0;\n    }\n}\n\n\nint\nngx_http_lua_ffi_set_resp_status_and_reason(ngx_http_request_t *r, int status,\n    const char *reason, size_t reason_len)\n{\n    u_char *buf;\n\n    if (r->connection->fd == (ngx_socket_t) -1) {\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    if (r->header_sent) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"attempt to set ngx.status after sending out \"\n                      \"response headers\");\n        return NGX_DECLINED;\n    }\n\n    /* per RFC-7230 sec 3.1.2, the status line must be 3 digits, it also makes\n     * buffer size calculation easier */\n    if (status < 100 || status > 999) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"invalid HTTP status code %d\", status);\n        return NGX_DECLINED;\n    }\n\n    r->headers_out.status = status;\n\n    if (r->err_status) {\n        r->err_status = 0;\n    }\n\n    if (status == 101) {\n        /*\n         * XXX work-around a bug in the Nginx core older than 1.5.5\n         * that 101 does not have a default status line\n         */\n\n        ngx_str_set(&r->headers_out.status_line, \"101 Switching Protocols\");\n\n    } else if (reason != NULL && reason_len > 0) {\n        reason_len += 4; /* \"ddd <reason>\" */\n        buf = ngx_palloc(r->pool, reason_len);\n        if (buf == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \"no memory\");\n            return NGX_DECLINED;\n        }\n\n        ngx_snprintf(buf, reason_len, \"%d %s\", status, reason);\n        r->headers_out.status_line.len = reason_len;\n        r->headers_out.status_line.data = buf;\n\n    } else {\n        r->headers_out.status_line.len = 0;\n    }\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_set_resp_status(ngx_http_request_t *r, int status)\n{\n    return ngx_http_lua_ffi_set_resp_status_and_reason(r, status, NULL, 0);\n}\n\n\nint\nngx_http_lua_ffi_req_is_internal(ngx_http_request_t *r)\n{\n    if (r->connection->fd == (ngx_socket_t) -1) {\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    return r->internal;\n}\n\n\nint\nngx_http_lua_ffi_is_subrequest(ngx_http_request_t *r)\n{\n    if (r->connection->fd == (ngx_socket_t) -1) {\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    return r != r->main;\n}\n\n\nint\nngx_http_lua_ffi_headers_sent(ngx_http_request_t *r)\n{\n    ngx_http_lua_ctx_t          *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_HTTP_LUA_FFI_NO_REQ_CTX;\n    }\n\n    if (r->connection->fd == (ngx_socket_t) -1) {\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    return r->header_sent ? 1 : 0;\n}\n\n\nint\nngx_http_lua_ffi_get_conf_env(u_char *name, u_char **env_buf, size_t *name_len)\n{\n    ngx_uint_t            i;\n    ngx_str_t            *var;\n    ngx_core_conf_t      *ccf;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,\n                                           ngx_core_module);\n\n    var = ccf->env.elts;\n\n    for (i = 0; i < ccf->env.nelts; i++) {\n        if (var[i].data[var[i].len] == '='\n            && ngx_strncmp(name, var[i].data, var[i].len) == 0)\n        {\n            *env_buf = var[i].data;\n            *name_len = var[i].len;\n\n            return NGX_OK;\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_misc.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_MISC_H_INCLUDED_\n#define _NGX_HTTP_LUA_MISC_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_req_misc_api(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_MISC_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_module.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_directive.h\"\n#include \"ngx_http_lua_capturefilter.h\"\n#include \"ngx_http_lua_precontentby.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_server_rewriteby.h\"\n#include \"ngx_http_lua_rewriteby.h\"\n#include \"ngx_http_lua_accessby.h\"\n#include \"ngx_http_lua_logby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_headerfilterby.h\"\n#include \"ngx_http_lua_bodyfilterby.h\"\n#include \"ngx_http_lua_initby.h\"\n#include \"ngx_http_lua_initworkerby.h\"\n#include \"ngx_http_lua_exitworkerby.h\"\n#include \"ngx_http_lua_probe.h\"\n#include \"ngx_http_lua_semaphore.h\"\n#include \"ngx_http_lua_balancer.h\"\n#include \"ngx_http_lua_ssl_client_helloby.h\"\n#include \"ngx_http_lua_ssl_certby.h\"\n#include \"ngx_http_lua_ssl_session_storeby.h\"\n#include \"ngx_http_lua_ssl_session_fetchby.h\"\n\n#include \"ngx_http_lua_proxy_ssl_certby.h\"\n#include \"ngx_http_lua_proxy_ssl_verifyby.h\"\n\n#include \"ngx_http_lua_headers.h\"\n#include \"ngx_http_lua_headers_out.h\"\n#if !(NGX_WIN32)\n#include \"ngx_http_lua_pipe.h\"\n#endif\n\n#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER > 0x101010afL\n#define HAVE_SSL_KEY_LOG 1\n#endif\n\n\nstatic void *ngx_http_lua_create_main_conf(ngx_conf_t *cf);\nstatic char *ngx_http_lua_init_main_conf(ngx_conf_t *cf, void *conf);\nstatic void *ngx_http_lua_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_http_lua_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic void *ngx_http_lua_create_loc_conf(ngx_conf_t *cf);\n\nstatic char *ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic ngx_int_t ngx_http_lua_init(ngx_conf_t *cf);\nstatic char *ngx_http_lua_lowat_check(ngx_conf_t *cf, void *post, void *data);\n#if (NGX_HTTP_SSL)\nstatic ngx_int_t ngx_http_lua_merge_ssl(ngx_conf_t *cf,\n    ngx_http_lua_loc_conf_t *conf, ngx_http_lua_loc_conf_t *prev);\nstatic ngx_int_t ngx_http_lua_set_ssl(ngx_conf_t *cf,\n    ngx_http_lua_loc_conf_t *llcf);\n#ifdef HAVE_SSL_KEY_LOG\nstatic void key_log_callback(const ngx_ssl_conn_t *ssl_conn,\n    const char *line);\nstatic void ngx_http_lua_ssl_cleanup_key_log(void *data);\nstatic ngx_int_t ngx_http_lua_ssl_key_log(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_str_t *file);\n#endif\n#if (nginx_version >= 1019004)\nstatic char *ngx_http_lua_ssl_conf_command_check(ngx_conf_t *cf, void *post,\n    void *data);\n#endif\n#endif\nstatic char *ngx_http_lua_malloc_trim(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#if (NGX_PCRE2)\nextern void ngx_http_lua_regex_cleanup(void *data);\n#endif\n\n\nstatic ngx_conf_post_t  ngx_http_lua_lowat_post =\n    { ngx_http_lua_lowat_check };\n\n\nstatic volatile ngx_cycle_t  *ngx_http_lua_prev_cycle = NULL;\n\n\n#if (NGX_HTTP_SSL)\n\nstatic ngx_conf_bitmask_t  ngx_http_lua_ssl_protocols[] = {\n    { ngx_string(\"SSLv2\"), NGX_SSL_SSLv2 },\n    { ngx_string(\"SSLv3\"), NGX_SSL_SSLv3 },\n    { ngx_string(\"TLSv1\"), NGX_SSL_TLSv1 },\n    { ngx_string(\"TLSv1.1\"), NGX_SSL_TLSv1_1 },\n    { ngx_string(\"TLSv1.2\"), NGX_SSL_TLSv1_2 },\n#ifdef NGX_SSL_TLSv1_3\n    { ngx_string(\"TLSv1.3\"), NGX_SSL_TLSv1_3 },\n#endif\n    { ngx_null_string, 0 }\n};\n\n#if (nginx_version >= 1019004)\nstatic ngx_conf_post_t  ngx_http_lua_ssl_conf_command_post =\n    { ngx_http_lua_ssl_conf_command_check };\n#endif\n\n#endif\n\n\nstatic ngx_command_t ngx_http_lua_cmds[] = {\n\n    { ngx_string(\"lua_load_resty_core\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,\n      ngx_http_lua_load_resty_core,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"lua_thread_cache_max_entries\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_lua_main_conf_t, lua_thread_cache_max_entries),\n      NULL },\n\n    { ngx_string(\"lua_max_running_timers\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_lua_main_conf_t, max_running_timers),\n      NULL },\n\n    { ngx_string(\"lua_max_pending_timers\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_lua_main_conf_t, max_pending_timers),\n      NULL },\n\n    { ngx_string(\"lua_shared_dict\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,\n      ngx_http_lua_shared_dict,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"lua_capture_error_log\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_capture_error_log,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"lua_sa_restart\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_lua_main_conf_t, set_sa_restart),\n      NULL },\n\n    { ngx_string(\"lua_regex_cache_max_entries\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_regex_cache_max_entries,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n#if (NGX_PCRE)\n      offsetof(ngx_http_lua_main_conf_t, regex_cache_max_entries),\n#else\n      0,\n#endif\n      NULL },\n\n    { ngx_string(\"lua_regex_match_limit\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_regex_match_limit,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n#if (NGX_PCRE)\n      offsetof(ngx_http_lua_main_conf_t, regex_match_limit),\n#else\n      0,\n#endif\n      NULL },\n\n    { ngx_string(\"lua_package_cpath\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_package_cpath,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"lua_package_path\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_package_path,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"lua_code_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_FLAG,\n      ngx_http_lua_code_cache,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, enable_code_cache),\n      NULL },\n\n    { ngx_string(\"lua_need_request_body\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n      |NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, force_read_body),\n      NULL },\n\n    { ngx_string(\"lua_transform_underscores_in_response_headers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, transform_underscores_in_resp_headers),\n      NULL },\n\n     { ngx_string(\"lua_socket_log_errors\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, log_socket_errors),\n      NULL },\n\n    { ngx_string(\"init_by_lua_block\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_init_by_lua_block,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_init_by_inline },\n\n    { ngx_string(\"init_by_lua\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_init_by_lua,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_init_by_inline },\n\n    { ngx_string(\"init_by_lua_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_init_by_lua,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_init_by_file },\n\n    { ngx_string(\"init_worker_by_lua_block\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_init_worker_by_lua_block,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_init_worker_by_inline },\n\n    { ngx_string(\"init_worker_by_lua\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_init_worker_by_lua,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_init_worker_by_inline },\n\n    { ngx_string(\"init_worker_by_lua_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_init_worker_by_lua,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_init_worker_by_file },\n\n    { ngx_string(\"exit_worker_by_lua_block\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_exit_worker_by_lua_block,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_exit_worker_by_inline },\n\n    { ngx_string(\"exit_worker_by_lua_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_exit_worker_by_lua,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_exit_worker_by_file },\n\n#if defined(NDK) && NDK\n    /* set_by_lua_block $res { inline Lua code } */\n    { ngx_string(\"set_by_lua_block\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                       |NGX_CONF_TAKE1|NGX_CONF_BLOCK,\n      ngx_http_lua_set_by_lua_block,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_filter_set_by_lua_inline },\n\n    /* set_by_lua $res <inline script> [$arg1 [$arg2 [...]]] */\n    { ngx_string(\"set_by_lua\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                       |NGX_CONF_2MORE,\n      ngx_http_lua_set_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_filter_set_by_lua_inline },\n\n    /* set_by_lua_file $res rel/or/abs/path/to/script [$arg1 [$arg2 [..]]] */\n    { ngx_string(\"set_by_lua_file\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                       |NGX_CONF_2MORE,\n      ngx_http_lua_set_by_lua_file,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_filter_set_by_lua_file },\n#endif\n\n    /* server_rewrite_by_lua_block { <inline script> } */\n    { ngx_string(\"server_rewrite_by_lua_block\"),\n        NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n        ngx_http_lua_server_rewrite_by_lua_block,\n        NGX_HTTP_SRV_CONF_OFFSET,\n        0,\n        (void *) ngx_http_lua_server_rewrite_handler_inline },\n\n    /* server_rewrite_by_lua_file filename; */\n    { ngx_string(\"server_rewrite_by_lua_file\"),\n        NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n        ngx_http_lua_server_rewrite_by_lua,\n        NGX_HTTP_SRV_CONF_OFFSET,\n        0,\n        (void *) ngx_http_lua_server_rewrite_handler_file },\n\n    /* rewrite_by_lua \"<inline script>\" */\n    { ngx_string(\"rewrite_by_lua\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_lua_rewrite_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_rewrite_handler_inline },\n\n    /* rewrite_by_lua_block { <inline script> } */\n    { ngx_string(\"rewrite_by_lua_block\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_rewrite_by_lua_block,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_rewrite_handler_inline },\n\n    /* access_by_lua \"<inline script>\" */\n    { ngx_string(\"access_by_lua\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_lua_access_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_access_handler_inline },\n\n    /* access_by_lua_block { <inline script> } */\n    { ngx_string(\"access_by_lua_block\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_access_by_lua_block,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_access_handler_inline },\n\n    /* precontent_by_lua_block { <inline script> } */\n    { ngx_string(\"precontent_by_lua_block\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_precontent_by_lua_block,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_precontent_handler_inline },\n\n    /* precontent_by_file filename; */\n    { ngx_string(\"precontent_by_lua_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_lua_precontent_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_precontent_handler_file },\n\n    { ngx_string(\"precontent_by_lua_no_postpone\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_lua_main_conf_t, postponed_to_precontent_phase_end),\n      NULL },\n\n    /* content_by_lua \"<inline script>\" */\n    { ngx_string(\"content_by_lua\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_content_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_content_handler_inline },\n\n    /* content_by_lua_block { <inline script> } */\n    { ngx_string(\"content_by_lua_block\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_content_by_lua_block,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_content_handler_inline },\n\n    /* log_by_lua <inline script> */\n    { ngx_string(\"log_by_lua\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_lua_log_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_log_handler_inline },\n\n    /* log_by_lua_block { <inline script> } */\n    { ngx_string(\"log_by_lua_block\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_log_by_lua_block,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_log_handler_inline },\n\n    { ngx_string(\"rewrite_by_lua_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_lua_rewrite_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_rewrite_handler_file },\n\n    { ngx_string(\"rewrite_by_lua_no_postpone\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_lua_main_conf_t, postponed_to_rewrite_phase_end),\n      NULL },\n\n    { ngx_string(\"access_by_lua_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_lua_access_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_access_handler_file },\n\n    { ngx_string(\"access_by_lua_no_postpone\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_lua_main_conf_t, postponed_to_access_phase_end),\n      NULL },\n\n    /* content_by_lua_file rel/or/abs/path/to/script */\n    { ngx_string(\"content_by_lua_file\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_content_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_content_handler_file },\n\n    { ngx_string(\"log_by_lua_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_lua_log_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_log_handler_file },\n\n    /* header_filter_by_lua <inline script> */\n    { ngx_string(\"header_filter_by_lua\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_lua_header_filter_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_header_filter_inline },\n\n    /* header_filter_by_lua_block { <inline script> } */\n    { ngx_string(\"header_filter_by_lua_block\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_header_filter_by_lua_block,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_header_filter_inline },\n\n    { ngx_string(\"header_filter_by_lua_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_lua_header_filter_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_header_filter_file },\n\n    { ngx_string(\"body_filter_by_lua\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_lua_body_filter_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_body_filter_inline },\n\n    /* body_filter_by_lua_block { <inline script> } */\n    { ngx_string(\"body_filter_by_lua_block\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_body_filter_by_lua_block,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_body_filter_inline },\n\n    { ngx_string(\"body_filter_by_lua_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_lua_body_filter_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_body_filter_file },\n\n    { ngx_string(\"balancer_by_lua_block\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_balancer_by_lua_block,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_balancer_handler_inline },\n\n    { ngx_string(\"balancer_by_lua_file\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_balancer_by_lua,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_balancer_handler_file },\n\n    { ngx_string(\"balancer_keepalive\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_balancer_keepalive,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_lua_srv_conf_t, balancer.max_cached),\n      NULL },\n\n    { ngx_string(\"lua_socket_keepalive_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n          |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, keepalive_timeout),\n      NULL },\n\n    { ngx_string(\"lua_socket_connect_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n          |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, connect_timeout),\n      NULL },\n\n    { ngx_string(\"lua_socket_send_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n          |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, send_timeout),\n      NULL },\n\n    { ngx_string(\"lua_socket_send_lowat\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n          |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, send_lowat),\n      &ngx_http_lua_lowat_post },\n\n    { ngx_string(\"lua_socket_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n          |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, buffer_size),\n      NULL },\n\n    { ngx_string(\"lua_socket_pool_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n                        |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, pool_size),\n      NULL },\n\n    { ngx_string(\"lua_socket_read_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n          |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, read_timeout),\n      NULL },\n\n    { ngx_string(\"lua_http10_buffering\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, http10_buffering),\n      NULL },\n\n    { ngx_string(\"lua_check_client_abort\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, check_client_abort),\n      NULL },\n\n    { ngx_string(\"lua_use_default_type\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, use_default_type),\n      NULL },\n\n#if (NGX_HTTP_SSL)\n\n    { ngx_string(\"lua_ssl_protocols\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_conf_set_bitmask_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, ssl_protocols),\n      &ngx_http_lua_ssl_protocols },\n\n    { ngx_string(\"lua_ssl_ciphers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, ssl_ciphers),\n      NULL },\n\n    { ngx_string(\"ssl_client_hello_by_lua_block\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_ssl_client_hello_by_lua_block,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_ssl_client_hello_handler_inline },\n\n    { ngx_string(\"ssl_client_hello_by_lua_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_ssl_client_hello_by_lua,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_ssl_client_hello_handler_file },\n\n    { ngx_string(\"ssl_certificate_by_lua_block\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_ssl_cert_by_lua_block,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_ssl_cert_handler_inline },\n\n    { ngx_string(\"ssl_certificate_by_lua_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_ssl_cert_by_lua,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_ssl_cert_handler_file },\n\n    { ngx_string(\"ssl_session_store_by_lua_block\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_ssl_sess_store_by_lua_block,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_ssl_sess_store_handler_inline },\n\n    { ngx_string(\"ssl_session_store_by_lua_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_ssl_sess_store_by_lua,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_ssl_sess_store_handler_file },\n\n    { ngx_string(\"ssl_session_fetch_by_lua_block\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_ssl_sess_fetch_by_lua_block,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_ssl_sess_fetch_handler_inline },\n\n    { ngx_string(\"ssl_session_fetch_by_lua_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_ssl_sess_fetch_by_lua,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_ssl_sess_fetch_handler_file },\n\n#if HAVE_LUA_PROXY_SSL\n    /* same context as proxy_pass directive */\n    { ngx_string(\"proxy_ssl_certificate_by_lua_block\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_proxy_ssl_cert_by_lua_block,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_proxy_ssl_cert_handler_inline },\n\n    { ngx_string(\"proxy_ssl_certificate_by_lua_file\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_proxy_ssl_cert_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_proxy_ssl_cert_handler_file },\n\n    /* same context as proxy_pass directive */\n    { ngx_string(\"proxy_ssl_verify_by_lua_block\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_lua_proxy_ssl_verify_by_lua_block,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_proxy_ssl_verify_handler_inline },\n\n    { ngx_string(\"proxy_ssl_verify_by_lua_file\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_proxy_ssl_verify_by_lua,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) ngx_http_lua_proxy_ssl_verify_handler_file },\n\n    { ngx_string(\"lua_upstream_skip_openssl_default_verify\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, upstream_skip_openssl_default_verify),\n      NULL },\n#endif\n\n    { ngx_string(\"lua_ssl_verify_depth\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, ssl_verify_depth),\n      NULL },\n\n    { ngx_string(\"lua_ssl_certificate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_array_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, ssl_certificates),\n      NULL },\n\n    { ngx_string(\"lua_ssl_certificate_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_array_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, ssl_certificate_keys),\n      NULL },\n\n    { ngx_string(\"lua_ssl_trusted_certificate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, ssl_trusted_certificate),\n      NULL },\n\n    { ngx_string(\"lua_ssl_crl\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, ssl_crl),\n      NULL },\n\n    { ngx_string(\"lua_ssl_key_log\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, ssl_key_log),\n      NULL },\n\n#if (nginx_version >= 1019004)\n    { ngx_string(\"lua_ssl_conf_command\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_keyval_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_lua_loc_conf_t, ssl_conf_commands),\n      &ngx_http_lua_ssl_conf_command_post },\n#endif\n#endif  /* NGX_HTTP_SSL */\n\n     { ngx_string(\"lua_malloc_trim\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_lua_malloc_trim,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"lua_worker_thread_vm_pool_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_lua_main_conf_t, worker_thread_vm_pool_size),\n      NULL },\n\n    ngx_null_command\n};\n\n\nstatic ngx_http_module_t ngx_http_lua_module_ctx = {\n    NULL,                             /*  preconfiguration */\n    ngx_http_lua_init,                /*  postconfiguration */\n\n    ngx_http_lua_create_main_conf,    /*  create main configuration */\n    ngx_http_lua_init_main_conf,      /*  init main configuration */\n\n    ngx_http_lua_create_srv_conf,     /*  create server configuration */\n    ngx_http_lua_merge_srv_conf,      /*  merge server configuration */\n\n    ngx_http_lua_create_loc_conf,     /*  create location configuration */\n    ngx_http_lua_merge_loc_conf       /*  merge location configuration */\n};\n\n\nngx_module_t ngx_http_lua_module = {\n    NGX_MODULE_V1,\n    &ngx_http_lua_module_ctx,   /*  module context */\n    ngx_http_lua_cmds,          /*  module directives */\n    NGX_HTTP_MODULE,            /*  module type */\n    NULL,                       /*  init master */\n    NULL,                       /*  init module */\n    ngx_http_lua_init_worker,   /*  init process */\n    NULL,                       /*  init thread */\n    NULL,                       /*  exit thread */\n    ngx_http_lua_exit_worker,   /*  exit process */\n    NULL,                       /*  exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\nstatic ngx_int_t\nngx_http_lua_init(ngx_conf_t *cf)\n{\n    int                         multi_http_blocks;\n    ngx_int_t                   rc;\n    ngx_array_t                *arr;\n    ngx_http_handler_pt        *h;\n    volatile ngx_cycle_t       *saved_cycle;\n    ngx_http_core_main_conf_t  *cmcf;\n    ngx_http_lua_main_conf_t   *lmcf;\n    ngx_pool_cleanup_t         *cln;\n    ngx_str_t                   name = ngx_string(\"host\");\n\n    if (ngx_process == NGX_PROCESS_SIGNALLER || ngx_test_config) {\n        return NGX_OK;\n    }\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);\n\n    lmcf->host_var_index = ngx_http_get_variable_index(cf, &name);\n    if (lmcf->host_var_index == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_lua_prev_cycle != ngx_cycle) {\n        ngx_http_lua_prev_cycle = ngx_cycle;\n        multi_http_blocks = 0;\n\n    } else {\n        multi_http_blocks = 1;\n    }\n\n    if (multi_http_blocks || lmcf->requires_capture_filter) {\n        rc = ngx_http_lua_capture_filter_init(cf);\n        if (rc != NGX_OK) {\n            return rc;\n        }\n    }\n\n    if (lmcf->postponed_to_rewrite_phase_end == NGX_CONF_UNSET) {\n        lmcf->postponed_to_rewrite_phase_end = 0;\n    }\n\n    if (lmcf->postponed_to_access_phase_end == NGX_CONF_UNSET) {\n        lmcf->postponed_to_access_phase_end = 0;\n    }\n\n    if (lmcf->postponed_to_precontent_phase_end == NGX_CONF_UNSET) {\n        lmcf->postponed_to_precontent_phase_end = 0;\n    }\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    if (lmcf->requires_server_rewrite) {\n        h = ngx_array_push(\n          &cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers);\n        if (h == NULL) {\n            return NGX_ERROR;\n        }\n\n        *h = ngx_http_lua_server_rewrite_handler;\n    }\n\n    if (lmcf->requires_rewrite) {\n        h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers);\n        if (h == NULL) {\n            return NGX_ERROR;\n        }\n\n        *h = ngx_http_lua_rewrite_handler;\n    }\n\n    if (lmcf->requires_access) {\n        h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);\n        if (h == NULL) {\n            return NGX_ERROR;\n        }\n\n        *h = ngx_http_lua_access_handler;\n    }\n\n    if (lmcf->requires_precontent) {\n        h = ngx_array_push(&cmcf->phases[NGX_HTTP_PRECONTENT_PHASE].handlers);\n        if (h == NULL) {\n            return NGX_ERROR;\n        }\n\n        *h = ngx_http_lua_precontent_handler;\n    }\n\n    dd(\"requires log: %d\", (int) lmcf->requires_log);\n\n    if (lmcf->requires_log) {\n        arr = &cmcf->phases[NGX_HTTP_LOG_PHASE].handlers;\n        h = ngx_array_push(arr);\n        if (h == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (arr->nelts > 1) {\n            h = arr->elts;\n            ngx_memmove(&h[1], h,\n                        (arr->nelts - 1) * sizeof(ngx_http_handler_pt));\n        }\n\n        *h = ngx_http_lua_log_handler;\n    }\n\n    if (multi_http_blocks || lmcf->requires_header_filter) {\n        rc = ngx_http_lua_header_filter_init();\n        if (rc != NGX_OK) {\n            return rc;\n        }\n    }\n\n    if (multi_http_blocks || lmcf->requires_body_filter) {\n        rc = ngx_http_lua_body_filter_init();\n        if (rc != NGX_OK) {\n            return rc;\n        }\n    }\n\n    /* add the cleanup of semaphores after the lua_close */\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln->data = lmcf;\n    cln->handler = ngx_http_lua_sema_mm_cleanup;\n\n#if (NGX_PCRE2)\n    /* add the cleanup of pcre2 regex */\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln->data = lmcf;\n    cln->handler = ngx_http_lua_regex_cleanup;\n#endif\n\n#ifdef HAVE_NGX_LUA_PIPE\n    ngx_http_lua_pipe_init();\n#endif\n\n#if (nginx_version >= 1011011)\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln->data = lmcf;\n    cln->handler = ngx_http_lua_ngx_raw_header_cleanup;\n#endif\n\n    if (lmcf->lua == NULL) {\n        dd(\"initializing lua vm\");\n\n#ifndef OPENRESTY_LUAJIT\n        if (ngx_process != NGX_PROCESS_SIGNALLER && !ngx_test_config) {\n            ngx_log_error(NGX_LOG_ALERT, cf->log, 0,\n                          \"detected a LuaJIT version which is not OpenResty's\"\n                          \"; many optimizations will be disabled and \"\n                          \"performance will be compromised (see \"\n                          \"https://github.com/openresty/luajit2 for \"\n                          \"OpenResty's LuaJIT or, even better, consider using \"\n                          \"the OpenResty releases from https://openresty.org/\"\n                          \"en/download.html)\");\n        }\n#else\n#   if !defined(HAVE_LUA_RESETTHREAD)\n        ngx_log_error(NGX_LOG_ALERT, cf->log, 0,\n                      \"detected an old version of OpenResty's LuaJIT missing \"\n                      \"the lua_resetthread API and thus the \"\n                      \"performance will be compromised; please upgrade to the \"\n                      \"latest version of OpenResty's LuaJIT: \"\n                      \"https://github.com/openresty/luajit2\");\n#   endif\n#   if !defined(HAVE_LUA_EXDATA2)\n        ngx_log_error(NGX_LOG_ALERT, cf->log, 0,\n                      \"detected an old version of OpenResty's LuaJIT missing \"\n                      \"the exdata2 API and thus the \"\n                      \"performance will be compromised; please upgrade to the \"\n                      \"latest version of OpenResty's LuaJIT: \"\n                      \"https://github.com/openresty/luajit2\");\n#   endif\n#endif\n\n        ngx_http_lua_content_length_hash =\n                                  ngx_http_lua_hash_literal(\"content-length\");\n        ngx_http_lua_location_hash = ngx_http_lua_hash_literal(\"location\");\n\n        rc = ngx_http_lua_init_vm(&lmcf->lua, NULL, cf->cycle, cf->pool,\n                                  lmcf, cf->log, NULL);\n        if (rc != NGX_OK) {\n            if (rc == NGX_DECLINED) {\n                ngx_http_lua_assert(lmcf->lua != NULL);\n\n                ngx_conf_log_error(NGX_LOG_ALERT, cf, 0,\n                                   \"failed to load the 'resty.core' module \"\n                                   \"(https://github.com/openresty/lua-resty\"\n                                   \"-core); ensure you are using an OpenResty \"\n                                   \"release from https://openresty.org/en/\"\n                                   \"download.html (reason: %s)\",\n                                   lua_tostring(lmcf->lua, -1));\n\n            } else {\n                /* rc == NGX_ERROR */\n                ngx_conf_log_error(NGX_LOG_ALERT, cf, 0,\n                                   \"failed to initialize Lua VM\");\n            }\n\n            return NGX_ERROR;\n        }\n\n        /* rc == NGX_OK */\n\n        ngx_http_lua_assert(lmcf->lua != NULL);\n\n        if (!lmcf->requires_shm && lmcf->init_handler) {\n            saved_cycle = ngx_cycle;\n            ngx_cycle = cf->cycle;\n\n            rc = lmcf->init_handler(cf->log, lmcf, lmcf->lua);\n\n            ngx_cycle = saved_cycle;\n\n            if (rc != NGX_OK) {\n                /* an error happened */\n                return NGX_ERROR;\n            }\n        }\n\n        dd(\"Lua VM initialized!\");\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_lua_lowat_check(ngx_conf_t *cf, void *post, void *data)\n{\n#if (NGX_FREEBSD)\n    ssize_t *np = data;\n\n    if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"lua_send_lowat\\\" must be less than %d \"\n                           \"(sysctl net.inet.tcp.sendspace)\",\n                           ngx_freebsd_net_inet_tcp_sendspace);\n\n        return NGX_CONF_ERROR;\n    }\n\n#elif !(NGX_HAVE_SO_SNDLOWAT)\n    ssize_t *np = data;\n\n    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                       \"\\\"lua_send_lowat\\\" is not supported, ignored\");\n\n    *np = 0;\n\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_lua_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_int_t       rc;\n\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    lmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_lua_main_conf_t));\n    if (lmcf == NULL) {\n        return NULL;\n    }\n\n    /* set by ngx_pcalloc:\n     *      lmcf->lua = NULL;\n     *      lmcf->lua_path = { 0, NULL };\n     *      lmcf->lua_cpath = { 0, NULL };\n     *      lmcf->pending_timers = 0;\n     *      lmcf->running_timers = 0;\n     *      lmcf->watcher = NULL;\n     *      lmcf->regex_cache_entries = 0;\n     *      lmcf->jit_stack = NULL;\n     *      lmcf->shm_zones = NULL;\n     *      lmcf->init_handler = NULL;\n     *      lmcf->init_src = { 0, NULL };\n     *      lmcf->init_chunkname = NULL;\n     *      lmcf->init_worker_handler = NULL;\n     *      lmcf->init_worker_src = { 0, NULL };\n     *      lmcf->init_worker_chunkname = NULL;\n     *      lmcf->exit_worker_handler = NULL;\n     *      lmcf->exit_worker_src = { 0, NULL };\n     *      lmcf->exit_worker_chunkname = NULL;\n     *      lmcf->shm_zones_inited = 0;\n     *      lmcf->shdict_zones = NULL;\n     *      lmcf->preload_hooks = NULL;\n     *      lmcf->requires_header_filter = 0;\n     *      lmcf->requires_body_filter = 0;\n     *      lmcf->requires_capture_filter = 0;\n     *      lmcf->requires_rewrite = 0;\n     *      lmcf->requires_access = 0;\n     *      lmcf->requires_log = 0;\n     *      lmcf->requires_shm = 0;\n     */\n\n    lmcf->pool = cf->pool;\n    lmcf->max_pending_timers = NGX_CONF_UNSET;\n    lmcf->max_running_timers = NGX_CONF_UNSET;\n    lmcf->lua_thread_cache_max_entries = NGX_CONF_UNSET;\n#if (NGX_PCRE)\n    lmcf->regex_cache_max_entries = NGX_CONF_UNSET;\n    lmcf->regex_match_limit = NGX_CONF_UNSET;\n#endif\n    lmcf->postponed_to_rewrite_phase_end = NGX_CONF_UNSET;\n    lmcf->postponed_to_access_phase_end = NGX_CONF_UNSET;\n    lmcf->postponed_to_precontent_phase_end = NGX_CONF_UNSET;\n\n    lmcf->set_sa_restart = NGX_CONF_UNSET;\n\n#if (NGX_HTTP_LUA_HAVE_MALLOC_TRIM)\n    lmcf->malloc_trim_cycle = NGX_CONF_UNSET_UINT;\n#endif\n\n    rc = ngx_http_lua_sema_mm_init(cf, lmcf);\n    if (rc != NGX_OK) {\n        return NULL;\n    }\n\n    lmcf->worker_thread_vm_pool_size = NGX_CONF_UNSET;\n\n    dd(\"nginx Lua module main config structure initialized!\");\n\n    return lmcf;\n}\n\n\nstatic char *\nngx_http_lua_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n#ifdef HAVE_LUA_RESETTHREAD\n    ngx_int_t                    i, n;\n    ngx_http_lua_thread_ref_t   *trefs;\n#endif\n\n    ngx_http_lua_main_conf_t     *lmcf = conf;\n\n    if (lmcf->lua_thread_cache_max_entries < 0) {\n        lmcf->lua_thread_cache_max_entries = 1024;\n\n#ifndef HAVE_LUA_RESETTHREAD\n\n    } else if (lmcf->lua_thread_cache_max_entries > 0) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"lua_thread_cache_max_entries has no effect when \"\n                      \"LuaJIT has no support for the lua_resetthread API \"\n                      \"(you forgot to use OpenResty's LuaJIT?)\");\n        return NGX_CONF_ERROR;\n\n#endif\n    }\n\n#if (NGX_PCRE)\n    if (lmcf->regex_cache_max_entries == NGX_CONF_UNSET) {\n        lmcf->regex_cache_max_entries = 1024;\n    }\n\n    if (lmcf->regex_match_limit == NGX_CONF_UNSET) {\n        lmcf->regex_match_limit = 0;\n    }\n#endif\n\n    if (lmcf->max_pending_timers == NGX_CONF_UNSET) {\n        lmcf->max_pending_timers = 1024;\n    }\n\n    if (lmcf->max_running_timers == NGX_CONF_UNSET) {\n        lmcf->max_running_timers = 256;\n    }\n\n#if (NGX_HTTP_LUA_HAVE_SA_RESTART)\n    if (lmcf->set_sa_restart == NGX_CONF_UNSET) {\n        lmcf->set_sa_restart = 1;\n    }\n#endif\n\n#if (NGX_HTTP_LUA_HAVE_MALLOC_TRIM)\n    if (lmcf->malloc_trim_cycle == NGX_CONF_UNSET_UINT) {\n        lmcf->malloc_trim_cycle = 1000;  /* number of reqs */\n    }\n#endif\n\n    lmcf->cycle = cf->cycle;\n\n    ngx_queue_init(&lmcf->free_lua_threads);\n    ngx_queue_init(&lmcf->cached_lua_threads);\n\n#ifdef HAVE_LUA_RESETTHREAD\n    n = lmcf->lua_thread_cache_max_entries;\n\n    if (n > 0) {\n        trefs = ngx_palloc(cf->pool, n * sizeof(ngx_http_lua_thread_ref_t));\n        if (trefs == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        for (i = 0; i < n; i++) {\n            trefs[i].ref = LUA_NOREF;\n            trefs[i].co = NULL;\n            ngx_queue_insert_head(&lmcf->free_lua_threads, &trefs[i].queue);\n        }\n    }\n#endif\n\n    if (lmcf->worker_thread_vm_pool_size == NGX_CONF_UNSET_UINT) {\n        lmcf->worker_thread_vm_pool_size = 10;\n    }\n\n    if (ngx_http_lua_init_builtin_headers_out(cf, lmcf) != NGX_OK) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"init header out error\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    dd(\"init built in headers out hash size: %ld\",\n       lmcf->builtin_headers_out.size);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_lua_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_http_lua_srv_conf_t     *lscf;\n\n    lscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_lua_srv_conf_t));\n    if (lscf == NULL) {\n        return NULL;\n    }\n\n    /* set by ngx_pcalloc:\n     *      lscf->srv.ssl_client_hello_handler = NULL;\n     *      lscf->srv.ssl_client_hello_src = { 0, NULL };\n     *      lscf->srv.ssl_client_hello_chunkname = NULL;\n     *      lscf->srv.ssl_client_hello_src_key = NULL;\n     *\n     *      lscf->srv.ssl_cert_handler = NULL;\n     *      lscf->srv.ssl_cert_src = { 0, NULL };\n     *      lscf->srv.ssl_cert_chunkname = NULL;\n     *      lscf->srv.ssl_cert_src_key = NULL;\n     *\n     *      lscf->srv.ssl_sess_store_handler = NULL;\n     *      lscf->srv.ssl_sess_store_src = { 0, NULL };\n     *      lscf->srv.ssl_sess_store_chunkname = NULL;\n     *      lscf->srv.ssl_sess_store_src_key = NULL;\n     *\n     *      lscf->srv.ssl_sess_fetch_handler = NULL;\n     *      lscf->srv.ssl_sess_fetch_src = { 0, NULL };\n     *      lscf->srv.ssl_sess_fetch_chunkname = NULL;\n     *      lscf->srv.ssl_sess_fetch_src_key = NULL;\n     *\n     *      lscf->srv.server_rewrite_handler = NULL;\n     *      lscf->srv.server_rewrite_src = { 0, NULL };\n     *      lscf->srv.server_rewrite_chunkname = NULL;\n     *      lscf->srv.server_rewrite_src_key = NULL;\n     *\n     *      lscf->balancer.original_init_upstream = NULL;\n     *      lscf->balancer.original_init_peer = NULL;\n     *      lscf->balancer.handler = NULL;\n     *      lscf->balancer.src = { 0, NULL };\n     *      lscf->balancer.chunkname = NULL;\n     *      lscf->balancer.src_key = NULL;\n     */\n\n#if (NGX_HTTP_SSL)\n    lscf->srv.ssl_client_hello_src_ref = LUA_REFNIL;\n    lscf->srv.ssl_cert_src_ref = LUA_REFNIL;\n    lscf->srv.ssl_sess_store_src_ref = LUA_REFNIL;\n    lscf->srv.ssl_sess_fetch_src_ref = LUA_REFNIL;\n#endif\n\n    lscf->srv.server_rewrite_src_ref = LUA_REFNIL;\n    lscf->balancer.src_ref = LUA_REFNIL;\n    lscf->balancer.max_cached = NGX_CONF_UNSET_UINT;\n    return lscf;\n}\n\n\nstatic char *\nngx_http_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_lua_srv_conf_t *conf = child;\n    ngx_http_lua_srv_conf_t *prev = parent;\n\n#if (NGX_HTTP_SSL)\n\n    ngx_http_ssl_srv_conf_t *sscf;\n\n    dd(\"merge srv conf\");\n\n    if (conf->srv.ssl_client_hello_src.len == 0) {\n        conf->srv.ssl_client_hello_src = prev->srv.ssl_client_hello_src;\n        conf->srv.ssl_client_hello_src_ref = prev->srv.ssl_client_hello_src_ref;\n        conf->srv.ssl_client_hello_src_key = prev->srv.ssl_client_hello_src_key;\n        conf->srv.ssl_client_hello_handler = prev->srv.ssl_client_hello_handler;\n        conf->srv.ssl_client_hello_chunkname\n            = prev->srv.ssl_client_hello_chunkname;\n    }\n\n    if (conf->srv.ssl_client_hello_src.len) {\n        sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module);\n        if (sscf == NULL || sscf->ssl.ctx == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no ssl configured for the server\");\n\n            return NGX_CONF_ERROR;\n        }\n#ifdef LIBRESSL_VERSION_NUMBER\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"LibreSSL does not support by ssl_client_hello_by_lua*\");\n        return NGX_CONF_ERROR;\n\n#else\n\n#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB\n\n        SSL_CTX_set_client_hello_cb(sscf->ssl.ctx,\n                                    ngx_http_lua_ssl_client_hello_handler,\n                                    NULL);\n\n#else\n\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"OpenSSL too old to support \"\n                      \"ssl_client_hello_by_lua*\");\n        return NGX_CONF_ERROR;\n\n#endif\n#endif\n    }\n\n    if (conf->srv.ssl_cert_src.len == 0) {\n        conf->srv.ssl_cert_src = prev->srv.ssl_cert_src;\n        conf->srv.ssl_cert_src_ref = prev->srv.ssl_cert_src_ref;\n        conf->srv.ssl_cert_src_key = prev->srv.ssl_cert_src_key;\n        conf->srv.ssl_cert_handler = prev->srv.ssl_cert_handler;\n        conf->srv.ssl_cert_chunkname = prev->srv.ssl_cert_chunkname;\n    }\n\n    if (conf->srv.ssl_cert_src.len) {\n        sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module);\n        if (sscf == NULL || sscf->ssl.ctx == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no ssl configured for the server\");\n\n            return NGX_CONF_ERROR;\n        }\n\n#ifdef LIBRESSL_VERSION_NUMBER\n\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"LibreSSL is not supported by ssl_certificate_by_lua*\");\n        return NGX_CONF_ERROR;\n\n#else\n\n#   if OPENSSL_VERSION_NUMBER >= 0x1000205fL\n\n        SSL_CTX_set_cert_cb(sscf->ssl.ctx, ngx_http_lua_ssl_cert_handler, NULL);\n\n#   else\n\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"OpenSSL too old to support ssl_certificate_by_lua*\");\n        return NGX_CONF_ERROR;\n\n#   endif\n\n#endif\n    }\n\n    if (conf->srv.ssl_sess_store_src.len == 0) {\n        conf->srv.ssl_sess_store_src = prev->srv.ssl_sess_store_src;\n        conf->srv.ssl_sess_store_src_ref = prev->srv.ssl_sess_store_src_ref;\n        conf->srv.ssl_sess_store_src_key = prev->srv.ssl_sess_store_src_key;\n        conf->srv.ssl_sess_store_handler = prev->srv.ssl_sess_store_handler;\n        conf->srv.ssl_sess_store_chunkname = prev->srv.ssl_sess_store_chunkname;\n    }\n\n    if (conf->srv.ssl_sess_store_src.len) {\n        sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module);\n        if (sscf && sscf->ssl.ctx) {\n#ifdef LIBRESSL_VERSION_NUMBER\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"LibreSSL is not supported by \"\n                          \"ssl_session_store_by_lua*\");\n\n            return NGX_CONF_ERROR;\n#else\n            SSL_CTX_sess_set_new_cb(sscf->ssl.ctx,\n                                    ngx_http_lua_ssl_sess_store_handler);\n#endif\n        }\n    }\n\n    if (conf->srv.ssl_sess_fetch_src.len == 0) {\n        conf->srv.ssl_sess_fetch_src = prev->srv.ssl_sess_fetch_src;\n        conf->srv.ssl_sess_fetch_src_ref = prev->srv.ssl_sess_fetch_src_ref;\n        conf->srv.ssl_sess_fetch_src_key = prev->srv.ssl_sess_fetch_src_key;\n        conf->srv.ssl_sess_fetch_handler = prev->srv.ssl_sess_fetch_handler;\n        conf->srv.ssl_sess_fetch_chunkname = prev->srv.ssl_sess_fetch_chunkname;\n    }\n\n    if (conf->srv.ssl_sess_fetch_src.len) {\n        sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module);\n        if (sscf && sscf->ssl.ctx) {\n#ifdef LIBRESSL_VERSION_NUMBER\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"LibreSSL is not supported by \"\n                          \"ssl_session_fetch_by_lua*\");\n\n            return NGX_CONF_ERROR;\n#else\n            SSL_CTX_sess_set_get_cb(sscf->ssl.ctx,\n                                    ngx_http_lua_ssl_sess_fetch_handler);\n#endif\n        }\n    }\n\n#endif  /* NGX_HTTP_SSL */\n\n    if (conf->srv.server_rewrite_src.value.len == 0) {\n        conf->srv.server_rewrite_src = prev->srv.server_rewrite_src;\n        conf->srv.server_rewrite_src_ref = prev->srv.server_rewrite_src_ref;\n        conf->srv.server_rewrite_src_key = prev->srv.server_rewrite_src_key;\n        conf->srv.server_rewrite_handler = prev->srv.server_rewrite_handler;\n        conf->srv.server_rewrite_chunkname\n            = prev->srv.server_rewrite_chunkname;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_lua_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_lua_loc_conf_t *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_lua_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /* set by ngx_pcalloc:\n     *      conf->access_src  = {{ 0, NULL }, NULL, NULL, NULL};\n     *      conf->access_src_key = NULL\n     *      conf->access_handler = NULL;\n     *      conf->access_chunkname = NULL;\n     *\n     *      conf->rewrite_src = {{ 0, NULL }, NULL, NULL, NULL};\n     *      conf->rewrite_src_key = NULL;\n     *      conf->rewrite_handler = NULL;\n     *      conf->rewrite_chunkname = NULL;\n     *\n     *      conf->precontent_src = {{ 0, NULL }, NULL, NULL, NULL};\n     *      conf->precontent_src_key = NULL;\n     *      conf->precontent_handler = NULL;\n     *      conf->precontent_chunkname = NULL;\n     *\n     *      conf->content_src = {{ 0, NULL }, NULL, NULL, NULL};\n     *      conf->content_src_key = NULL;\n     *      conf->content_handler = NULL;\n     *      conf->content_chunkname = NULL;\n     *\n     *      conf->log_src = {{ 0, NULL }, NULL, NULL, NULL};\n     *      conf->log_src_key = NULL;\n     *      conf->log_handler = NULL;\n     *      conf->log_chunkname = NULL;\n     *\n     *      conf->header_filter_src = {{ 0, NULL }, NULL, NULL, NULL};\n     *      conf->header_filter_src_key = NULL;\n     *      conf->header_filter_handler = NULL;\n     *      conf->header_filter_chunkname = NULL;\n     *\n     *      conf->body_filter_src = {{ 0, NULL }, NULL, NULL, NULL};\n     *      conf->body_filter_src_key = NULL;\n     *      conf->body_filter_handler = NULL;\n     *      conf->body_filter_chunkname = NULL;\n     *\n     *      conf->ssl = 0;\n     *      conf->ssl_protocols = 0;\n     *      conf->ssl_ciphers = { 0, NULL };\n     *      conf->ssl_trusted_certificate = { 0, NULL };\n     *      conf->ssl_crl = { 0, NULL };\n     *      conf->ssl_key_log = { 0, NULL };\n     *\n     *      conf->proxy_ssl_cert_handler = NULL;\n     *      conf->proxy_ssl_cert_src = { 0, NULL };\n     *      conf->proxy_ssl_cert_chunkname = NULL;\n     *      conf->proxy_ssl_cert_src_key = NULL;\n     *\n     *      conf->proxy_ssl_verify_handler = NULL;\n     *      conf->proxy_ssl_verify_src = { 0, NULL };\n     *      conf->proxy_ssl_verify_chunkname = NULL;\n     *      conf->proxy_ssl_verify_src_key = NULL;\n     */\n\n    conf->force_read_body    = NGX_CONF_UNSET;\n    conf->enable_code_cache  = NGX_CONF_UNSET;\n    conf->http10_buffering   = NGX_CONF_UNSET;\n    conf->check_client_abort = NGX_CONF_UNSET;\n    conf->use_default_type   = NGX_CONF_UNSET;\n\n    conf->keepalive_timeout = NGX_CONF_UNSET_MSEC;\n    conf->connect_timeout = NGX_CONF_UNSET_MSEC;\n    conf->send_timeout = NGX_CONF_UNSET_MSEC;\n    conf->read_timeout = NGX_CONF_UNSET_MSEC;\n    conf->send_lowat = NGX_CONF_UNSET_SIZE;\n    conf->buffer_size = NGX_CONF_UNSET_SIZE;\n    conf->pool_size = NGX_CONF_UNSET_UINT;\n\n    conf->transform_underscores_in_resp_headers = NGX_CONF_UNSET;\n    conf->log_socket_errors = NGX_CONF_UNSET;\n\n    conf->rewrite_src_ref = LUA_REFNIL;\n    conf->access_src_ref = LUA_REFNIL;\n    conf->precontent_src_ref = LUA_REFNIL;\n    conf->content_src_ref = LUA_REFNIL;\n    conf->header_filter_src_ref = LUA_REFNIL;\n    conf->body_filter_src_ref = LUA_REFNIL;\n    conf->log_src_ref = LUA_REFNIL;\n\n#if (NGX_HTTP_SSL)\n    conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;\n    conf->ssl_certificates = NGX_CONF_UNSET_PTR;\n    conf->ssl_certificate_keys = NGX_CONF_UNSET_PTR;\n#if (nginx_version >= 1019004)\n    conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;\n#endif\n#if HAVE_LUA_PROXY_SSL\n    conf->proxy_ssl_cert_src_ref = LUA_REFNIL;\n    conf->proxy_ssl_verify_src_ref = LUA_REFNIL;\n    conf->upstream_skip_openssl_default_verify = NGX_CONF_UNSET;\n#endif\n#endif\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_lua_loc_conf_t *prev = parent;\n    ngx_http_lua_loc_conf_t *conf = child;\n\n    if (conf->rewrite_src.value.len == 0) {\n        conf->rewrite_src = prev->rewrite_src;\n        conf->rewrite_handler = prev->rewrite_handler;\n        conf->rewrite_src_ref = prev->rewrite_src_ref;\n        conf->rewrite_src_key = prev->rewrite_src_key;\n        conf->rewrite_chunkname = prev->rewrite_chunkname;\n    }\n\n    if (conf->access_src.value.len == 0) {\n        conf->access_src = prev->access_src;\n        conf->access_handler = prev->access_handler;\n        conf->access_src_ref = prev->access_src_ref;\n        conf->access_src_key = prev->access_src_key;\n        conf->access_chunkname = prev->access_chunkname;\n    }\n\n    if (conf->precontent_src.value.len == 0) {\n        conf->precontent_src = prev->precontent_src;\n        conf->precontent_handler = prev->precontent_handler;\n        conf->precontent_src_ref = prev->precontent_src_ref;\n        conf->precontent_src_key = prev->precontent_src_key;\n        conf->precontent_chunkname = prev->precontent_chunkname;\n    }\n\n    if (conf->content_src.value.len == 0) {\n        conf->content_src = prev->content_src;\n        conf->content_handler = prev->content_handler;\n        conf->content_src_ref = prev->content_src_ref;\n        conf->content_src_key = prev->content_src_key;\n        conf->content_chunkname = prev->content_chunkname;\n    }\n\n    if (conf->log_src.value.len == 0) {\n        conf->log_src = prev->log_src;\n        conf->log_handler = prev->log_handler;\n        conf->log_src_ref = prev->log_src_ref;\n        conf->log_src_key = prev->log_src_key;\n        conf->log_chunkname = prev->log_chunkname;\n    }\n\n    if (conf->header_filter_src.value.len == 0) {\n        conf->header_filter_src = prev->header_filter_src;\n        conf->header_filter_handler = prev->header_filter_handler;\n        conf->header_filter_src_ref = prev->header_filter_src_ref;\n        conf->header_filter_src_key = prev->header_filter_src_key;\n        conf->header_filter_chunkname = prev->header_filter_chunkname;\n    }\n\n    if (conf->body_filter_src.value.len == 0) {\n        conf->body_filter_src = prev->body_filter_src;\n        conf->body_filter_handler = prev->body_filter_handler;\n        conf->body_filter_src_ref = prev->body_filter_src_ref;\n        conf->body_filter_src_key = prev->body_filter_src_key;\n        conf->body_filter_chunkname = prev->body_filter_chunkname;\n    }\n\n#if (NGX_HTTP_SSL)\n\n    if (ngx_http_lua_merge_ssl(cf, conf, prev) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,\n                                 (NGX_CONF_BITMASK_SET\n                                  |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1\n                                  |NGX_SSL_TLSv1_2\n#ifdef NGX_SSL_TLSv1_3\n                                  |NGX_SSL_TLSv1_3\n#endif\n                                  ));\n\n    ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,\n                             \"DEFAULT\");\n\n    ngx_conf_merge_uint_value(conf->ssl_verify_depth,\n                              prev->ssl_verify_depth, 1);\n    ngx_conf_merge_ptr_value(conf->ssl_certificates,\n                             prev->ssl_certificates, NULL);\n    ngx_conf_merge_ptr_value(conf->ssl_certificate_keys,\n                             prev->ssl_certificate_keys, NULL);\n    ngx_conf_merge_str_value(conf->ssl_trusted_certificate,\n                             prev->ssl_trusted_certificate, \"\");\n    ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, \"\");\n    ngx_conf_merge_str_value(conf->ssl_key_log, prev->ssl_key_log, \"\");\n\n#if (nginx_version >= 1019004)\n    ngx_conf_merge_ptr_value(conf->ssl_conf_commands, prev->ssl_conf_commands,\n                             NULL);\n#endif\n\n#if HAVE_LUA_PROXY_SSL\n    if (conf->proxy_ssl_cert_src.len == 0) {\n        conf->proxy_ssl_cert_src = prev->proxy_ssl_cert_src;\n        conf->proxy_ssl_cert_handler = prev->proxy_ssl_cert_handler;\n        conf->proxy_ssl_cert_src_ref = prev->proxy_ssl_cert_src_ref;\n        conf->proxy_ssl_cert_src_key = prev->proxy_ssl_cert_src_key;\n        conf->proxy_ssl_cert_chunkname = prev->proxy_ssl_cert_chunkname;\n    }\n\n    if (conf->proxy_ssl_cert_src.len) {\n        if (ngx_http_lua_proxy_ssl_cert_set_callback(cf) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (conf->proxy_ssl_verify_src.len == 0) {\n        conf->proxy_ssl_verify_src = prev->proxy_ssl_verify_src;\n        conf->proxy_ssl_verify_handler = prev->proxy_ssl_verify_handler;\n        conf->proxy_ssl_verify_src_ref = prev->proxy_ssl_verify_src_ref;\n        conf->proxy_ssl_verify_src_key = prev->proxy_ssl_verify_src_key;\n        conf->proxy_ssl_verify_chunkname = prev->proxy_ssl_verify_chunkname;\n    }\n\n    if (conf->proxy_ssl_verify_src.len) {\n        if (ngx_http_lua_proxy_ssl_verify_set_callback(cf) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    ngx_conf_merge_value(conf->upstream_skip_openssl_default_verify,\n                         prev->upstream_skip_openssl_default_verify, 0);\n#endif\n\n    if (ngx_http_lua_set_ssl(cf, conf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n#endif\n\n    ngx_conf_merge_value(conf->force_read_body, prev->force_read_body, 0);\n    ngx_conf_merge_value(conf->enable_code_cache, prev->enable_code_cache, 1);\n    ngx_conf_merge_value(conf->http10_buffering, prev->http10_buffering, 1);\n    ngx_conf_merge_value(conf->check_client_abort, prev->check_client_abort, 0);\n    ngx_conf_merge_value(conf->use_default_type, prev->use_default_type, 1);\n\n    ngx_conf_merge_msec_value(conf->keepalive_timeout,\n                              prev->keepalive_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->connect_timeout,\n                              prev->connect_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->send_timeout,\n                              prev->send_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->read_timeout,\n                              prev->read_timeout, 60000);\n\n    ngx_conf_merge_size_value(conf->send_lowat,\n                              prev->send_lowat, 0);\n\n    ngx_conf_merge_size_value(conf->buffer_size,\n                              prev->buffer_size,\n                              (size_t) ngx_pagesize);\n\n    ngx_conf_merge_uint_value(conf->pool_size, prev->pool_size, 30);\n\n    ngx_conf_merge_value(conf->transform_underscores_in_resp_headers,\n                         prev->transform_underscores_in_resp_headers, 1);\n\n    ngx_conf_merge_value(conf->log_socket_errors, prev->log_socket_errors, 1);\n\n    return NGX_CONF_OK;\n}\n\n\n#if (NGX_HTTP_SSL)\n\nstatic ngx_int_t\nngx_http_lua_merge_ssl(ngx_conf_t *cf,\n    ngx_http_lua_loc_conf_t *conf, ngx_http_lua_loc_conf_t *prev)\n{\n    ngx_uint_t  preserve;\n\n    if (conf->ssl_protocols == 0\n        && conf->ssl_ciphers.data == NULL\n        && conf->ssl_verify_depth == NGX_CONF_UNSET_UINT\n        && conf->ssl_certificates == NGX_CONF_UNSET_PTR\n        && conf->ssl_certificate_keys == NGX_CONF_UNSET_PTR\n        && conf->ssl_trusted_certificate.data == NULL\n        && conf->ssl_crl.data == NULL\n        && conf->ssl_key_log.data == NULL\n#if (nginx_version >= 1019004)\n        && conf->ssl_conf_commands == NGX_CONF_UNSET_PTR\n#endif\n       )\n    {\n        if (prev->ssl) {\n            conf->ssl = prev->ssl;\n            return NGX_OK;\n        }\n\n        preserve = 1;\n\n    } else {\n        preserve = 0;\n    }\n\n    conf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));\n    if (conf->ssl == NULL) {\n        return NGX_ERROR;\n    }\n\n    conf->ssl->log = cf->log;\n\n    /*\n     * special handling to preserve conf->ssl_* in the \"http\" section\n     * to inherit it to all servers\n     */\n\n    if (preserve) {\n        prev->ssl = conf->ssl;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_set_ssl(ngx_conf_t *cf, ngx_http_lua_loc_conf_t *llcf)\n{\n    ngx_pool_cleanup_t  *cln;\n\n    if (llcf->ssl->ctx) {\n        return NGX_OK;\n    }\n\n    if (llcf->ssl_certificates) {\n        if (llcf->ssl_certificate_keys == NULL\n            || llcf->ssl_certificate_keys->nelts\n            < llcf->ssl_certificates->nelts)\n        {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"lua_ssl_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\",\n                          ((ngx_str_t *) llcf->ssl_certificates->elts)\n                          + llcf->ssl_certificates->nelts - 1);\n            return NGX_ERROR;\n        }\n    }\n\n    if (ngx_ssl_create(llcf->ssl, llcf->ssl_protocols, NULL) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        ngx_ssl_cleanup_ctx(llcf->ssl);\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_ssl_cleanup_ctx;\n    cln->data = llcf->ssl;\n\n    if (SSL_CTX_set_cipher_list(llcf->ssl->ctx,\n                                (const char *) llcf->ssl_ciphers.data)\n        == 0)\n    {\n        ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"SSL_CTX_set_cipher_list(\\\"%V\\\") failed\",\n                      &llcf->ssl_ciphers);\n        return NGX_ERROR;\n    }\n\n    if (llcf->ssl_certificates\n        && ngx_ssl_certificates(cf, llcf->ssl,\n                                llcf->ssl_certificates,\n                                llcf->ssl_certificate_keys,\n                                NULL)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (llcf->ssl_trusted_certificate.len\n        && ngx_ssl_trusted_certificate(cf, llcf->ssl,\n                                       &llcf->ssl_trusted_certificate,\n                                       llcf->ssl_verify_depth)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    dd(\"ssl crl: %.*s\", (int) llcf->ssl_crl.len, llcf->ssl_crl.data);\n\n    if (ngx_ssl_crl(cf, llcf->ssl, &llcf->ssl_crl) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n#ifdef HAVE_SSL_KEY_LOG\n    if (ngx_http_lua_ssl_key_log(cf, llcf->ssl, &llcf->ssl_key_log)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n#endif\n\n#if (nginx_version >= 1019004)\n    if (ngx_ssl_conf_commands(cf, llcf->ssl, llcf->ssl_conf_commands)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n#endif\n\n    return NGX_OK;\n}\n\n\n#ifdef HAVE_SSL_KEY_LOG\nstatic void\nkey_log_callback(const ngx_ssl_conn_t *ssl_conn, const char *line)\n{\n    ngx_http_lua_ssl_key_log_t  *ssl_key_log;\n    ngx_connection_t            *c;\n\n    ssl_key_log = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn),\n                                      ngx_http_lua_ssl_key_log_index);\n    if (ssl_key_log == NULL) {\n        c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);\n        ngx_ssl_error(NGX_LOG_DEBUG, c->log, 0, \"get ssl key log failed\");\n\n        return;\n    }\n\n    (void) ngx_write_fd(ssl_key_log->fd, (void *) line, ngx_strlen(line));\n    (void) ngx_write_fd(ssl_key_log->fd, (void *) \"\\n\", 1);\n}\n\n\nstatic void\nngx_http_lua_ssl_cleanup_key_log(void *data)\n{\n    ngx_http_lua_ssl_key_log_t  *ssl_key_log = data;\n\n    if (ngx_close_file(ssl_key_log->fd) == NGX_FILE_ERROR) {\n        ngx_ssl_error(NGX_LOG_ALERT, ssl_key_log->ssl->log, 0,\n                      ngx_close_file_n \"(\\\"%V\\\") failed\", ssl_key_log->name);\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_lua_ssl_key_log(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)\n{\n    ngx_fd_t                     fd;\n    ngx_http_lua_ssl_key_log_t  *ssl_key_log;\n    ngx_pool_cleanup_t          *cln;\n\n    if (!file->len) {\n        return NGX_OK;\n    }\n\n    if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    /*\n     * append so that existing keylog file contents can be preserved\n     */\n    fd = ngx_open_file(file->data, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN,\n                       NGX_FILE_DEFAULT_ACCESS);\n    if (fd == NGX_INVALID_FILE) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, ngx_open_file_n\n                      \"(\\\"%V\\\") failed\", file);\n        return NGX_ERROR;\n    }\n\n    ssl_key_log = ngx_palloc(cf->pool, sizeof(ngx_http_lua_ssl_key_log_t));\n    if (ssl_key_log == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, \"ngx_pcalloc() failed\");\n        return NGX_ERROR;\n    }\n\n    ssl_key_log->ssl = ssl;\n    ssl_key_log->fd = fd;\n    ssl_key_log->name = *file;\n\n    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_http_lua_ssl_key_log_index,\n                            ssl_key_log) == 0)\n    {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_set_ex_data() failed\");\n        return NGX_ERROR;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        ngx_http_lua_ssl_cleanup_key_log(ssl_key_log);\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_http_lua_ssl_cleanup_key_log;\n    cln->data = ssl_key_log;\n\n    SSL_CTX_set_keylog_callback(ssl->ctx, key_log_callback);\n\n    return NGX_OK;\n}\n#endif\n\n\n#if (nginx_version >= 1019004)\nstatic char *\nngx_http_lua_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)\n{\n#ifndef SSL_CONF_FLAG_FILE\n    return \"is not supported on this platform\";\n#endif\n\n    return NGX_CONF_OK;\n}\n#endif\n\n#endif  /* NGX_HTTP_SSL */\n\n\nstatic char *\nngx_http_lua_malloc_trim(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n#if (NGX_HTTP_LUA_HAVE_MALLOC_TRIM)\n\n    ngx_int_t       nreqs;\n    ngx_str_t      *value;\n\n    ngx_http_lua_main_conf_t    *lmcf = conf;\n\n    value = cf->args->elts;\n\n    nreqs = ngx_atoi(value[1].data, value[1].len);\n    if (nreqs == NGX_ERROR) {\n        return \"invalid number in the 1st argument\";\n    }\n\n    lmcf->malloc_trim_cycle = (ngx_uint_t) nreqs;\n\n    if (nreqs == 0) {\n        return NGX_CONF_OK;\n    }\n\n    lmcf->requires_log = 1;\n\n#else\n\n    ngx_conf_log_error(NGX_LOG_WARN, cf, 0, \"lua_malloc_trim is not supported \"\n                       \"on this platform, ignored\");\n\n#endif\n    return NGX_CONF_OK;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_ndk.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_ndk.h\"\n#include \"ngx_http_lua_util.h\"\n\n\n#if defined(NDK) && NDK\n\n\nstatic ndk_set_var_value_pt ngx_http_lookup_ndk_set_var_directive(u_char *name,\n    size_t name_len);\n\n\nvoid\nngx_http_lua_inject_ndk_api(lua_State *L)\n{\n    lua_createtable(L, 0, 1 /* nrec */);    /* ndk.* */\n\n    lua_getglobal(L, \"package\"); /* ndk package */\n    lua_getfield(L, -1, \"loaded\"); /* ndk package loaded */\n    lua_pushvalue(L, -3); /* ndk package loaded ndk */\n    lua_setfield(L, -2, \"ndk\"); /* ndk package loaded */\n    lua_pop(L, 2);\n\n    lua_setglobal(L, \"ndk\");\n}\n\n\nstatic ndk_set_var_value_pt\nngx_http_lookup_ndk_set_var_directive(u_char *name,\n    size_t name_len)\n{\n    ndk_set_var_t           *filter;\n    ngx_uint_t               i;\n    ngx_module_t            *module;\n    ngx_module_t           **modules;\n    ngx_command_t           *cmd;\n\n#if (nginx_version >= 1009011)\n    modules = ngx_cycle->modules;\n#else\n    modules = ngx_modules;\n#endif\n\n    for (i = 0; modules[i]; i++) {\n        module = modules[i];\n        if (module->type != NGX_HTTP_MODULE) {\n            continue;\n        }\n\n        cmd = modules[i]->commands;\n        if (cmd == NULL) {\n            continue;\n        }\n\n        for ( /* void */ ; cmd->name.len; cmd++) {\n            if (cmd->set != ndk_set_var_value) {\n                continue;\n            }\n\n            filter = cmd->post;\n            if (filter == NULL) {\n                continue;\n            }\n\n            if (cmd->name.len != name_len\n                || ngx_strncmp(cmd->name.data, name, name_len) != 0)\n            {\n                continue;\n            }\n\n            return (ndk_set_var_value_pt)(filter->func);\n        }\n    }\n\n    return NULL;\n}\n\n\nint\nngx_http_lua_ffi_ndk_lookup_directive(const u_char *var_data,\n    size_t var_len, ndk_set_var_value_pt *func)\n{\n    *func = ngx_http_lookup_ndk_set_var_directive((u_char *) var_data, var_len);\n\n    if (*func == NULL) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_ndk_set_var_get(ngx_http_request_t *r,\n    ndk_set_var_value_pt func, const u_char *arg_data, size_t arg_len,\n    ngx_http_lua_ffi_str_t *value)\n{\n    ngx_int_t                            rc;\n    ngx_str_t                            res;\n    ngx_http_variable_value_t            arg;\n\n    ngx_memzero(&arg, sizeof(ngx_http_variable_value_t));\n    arg.valid = 1;\n\n    arg.data = (u_char *) arg_data;\n    arg.len = arg_len;\n\n    rc = func(r, &res, &arg);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    value->data = res.data;\n    value->len = res.len;\n    return NGX_OK;\n}\n\n\n#endif /* defined(NDK) && NDK */\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_ndk.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_NDK_H_INCLUDED_\n#define _NGX_HTTP_LUA_NDK_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\n#if defined(NDK) && NDK\nvoid ngx_http_lua_inject_ndk_api(lua_State *L);\n#endif\n\n\n#endif /* _NGX_HTTP_LUA_NDK_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_output.c",
    "content": "#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_output.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include <math.h>\n\n\nstatic int ngx_http_lua_ngx_say(lua_State *L);\nstatic int ngx_http_lua_ngx_print(lua_State *L);\nstatic int ngx_http_lua_ngx_flush(lua_State *L);\nstatic int ngx_http_lua_ngx_eof(lua_State *L);\nstatic int ngx_http_lua_ngx_send_headers(lua_State *L);\nstatic int ngx_http_lua_ngx_echo(lua_State *L, unsigned newline);\nstatic void ngx_http_lua_flush_cleanup(void *data);\n\n\nstatic int\nngx_http_lua_ngx_print(lua_State *L)\n{\n    dd(\"calling lua print\");\n    return ngx_http_lua_ngx_echo(L, 0);\n}\n\n\nstatic int\nngx_http_lua_ngx_say(lua_State *L)\n{\n    dd(\"calling\");\n    return ngx_http_lua_ngx_echo(L, 1);\n}\n\n\nstatic int\nngx_http_lua_ngx_echo(lua_State *L, unsigned newline)\n{\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    const char                  *p;\n    size_t                       len;\n    size_t                       size;\n    ngx_buf_t                   *b;\n    ngx_chain_t                 *cl;\n    ngx_int_t                    rc;\n    int                          i;\n    int                          nargs;\n    int                          type;\n    const char                  *msg;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request object found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_ACCESS\n                               | NGX_HTTP_LUA_CONTEXT_PRECONTENT\n                               | NGX_HTTP_LUA_CONTEXT_CONTENT);\n\n    if (ctx->acquired_raw_req_socket) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"raw request socket acquired\");\n        return 2;\n    }\n\n    if (r->header_only) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"header only\");\n        return 2;\n    }\n\n    if (ctx->eof) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"seen eof\");\n        return 2;\n    }\n\n    nargs = lua_gettop(L);\n    size = 0;\n\n    for (i = 1; i <= nargs; i++) {\n\n        type = lua_type(L, i);\n\n        switch (type) {\n            case LUA_TNUMBER:\n                size += ngx_http_lua_get_num_len(L, i);\n                break;\n\n            case LUA_TSTRING:\n\n                lua_tolstring(L, i, &len);\n                size += len;\n                break;\n\n            case LUA_TNIL:\n\n                size += sizeof(\"nil\") - 1;\n                break;\n\n            case LUA_TBOOLEAN:\n\n                if (lua_toboolean(L, i)) {\n                    size += sizeof(\"true\") - 1;\n\n                } else {\n                    size += sizeof(\"false\") - 1;\n                }\n\n                break;\n\n            case LUA_TTABLE:\n\n                size += ngx_http_lua_calc_strlen_in_table(L, i, i,\n                                                          0 /* strict */);\n                break;\n\n            case LUA_TLIGHTUSERDATA:\n\n                dd(\"userdata: %p\", lua_touserdata(L, i));\n\n                if (lua_touserdata(L, i) == NULL) {\n                    size += sizeof(\"null\") - 1;\n                    break;\n                }\n\n                continue;\n\n            default:\n\n                msg = lua_pushfstring(L, \"string, number, boolean, nil, \"\n                                      \"ngx.null, or array table expected, \"\n                                      \"but got %s\", lua_typename(L, type));\n\n                return luaL_argerror(L, i, msg);\n        }\n    }\n\n    if (newline) {\n        size += sizeof(\"\\n\") - 1;\n    }\n\n    if (size == 0) {\n        rc = ngx_http_lua_send_header_if_needed(r, ctx);\n        if (rc == NGX_ERROR || rc > NGX_OK) {\n            lua_pushnil(L);\n            lua_pushliteral(L, \"nginx output filter error\");\n            return 2;\n        }\n\n        lua_pushinteger(L, 1);\n        return 1;\n    }\n\n    ctx->seen_body_data = 1;\n\n    cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,\n                                         &ctx->free_bufs, size);\n\n    if (cl == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    b = cl->buf;\n\n    for (i = 1; i <= nargs; i++) {\n        type = lua_type(L, i);\n        switch (type) {\n            case LUA_TNUMBER:\n                b->last = ngx_http_lua_write_num(L, i, b->last);\n                break;\n\n            case LUA_TSTRING:\n                p = lua_tolstring(L, i, &len);\n                b->last = ngx_copy(b->last, (u_char *) p, len);\n                break;\n\n            case LUA_TNIL:\n                *b->last++ = 'n';\n                *b->last++ = 'i';\n                *b->last++ = 'l';\n                break;\n\n            case LUA_TBOOLEAN:\n                if (lua_toboolean(L, i)) {\n                    *b->last++ = 't';\n                    *b->last++ = 'r';\n                    *b->last++ = 'u';\n                    *b->last++ = 'e';\n\n                } else {\n                    *b->last++ = 'f';\n                    *b->last++ = 'a';\n                    *b->last++ = 'l';\n                    *b->last++ = 's';\n                    *b->last++ = 'e';\n                }\n\n                break;\n\n            case LUA_TTABLE:\n                b->last = ngx_http_lua_copy_str_in_table(L, i, b->last);\n                break;\n\n            case LUA_TLIGHTUSERDATA:\n                *b->last++ = 'n';\n                *b->last++ = 'u';\n                *b->last++ = 'l';\n                *b->last++ = 'l';\n                break;\n\n            default:\n                return luaL_error(L, \"impossible to reach here\");\n        }\n    }\n\n    if (newline) {\n        *b->last++ = '\\n';\n    }\n\n#if 0\n    if (b->last != b->end) {\n        return luaL_error(L, \"buffer error: %p != %p\", b->last, b->end);\n    }\n#endif\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   newline ? \"lua say response\" : \"lua print response\");\n\n    rc = ngx_http_lua_send_chain_link(r, ctx, cl);\n\n    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"nginx output filter error\");\n        return 2;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"%s has %sbusy bufs\",\n                   newline ? \"lua say response\" : \"lua print response\",\n                   ctx->busy_bufs != NULL ? \"\" : \"no \");\n\n    dd(\"downstream write: %d, buf len: %d\", (int) rc,\n       (int) (b->last - b->pos));\n\n    lua_pushinteger(L, 1);\n    return 1;\n}\n\n\nsize_t\nngx_http_lua_calc_strlen_in_table(lua_State *L, int index, int arg_i,\n    unsigned strict)\n{\n    double              key;\n    int                 max;\n    int                 i;\n    int                 type;\n    size_t              size;\n    size_t              len;\n    const char         *msg;\n\n    if (index < 0) {\n        index = lua_gettop(L) + index + 1;\n    }\n\n    dd(\"table index: %d\", index);\n\n    max = 0;\n\n    lua_pushnil(L); /* stack: table key */\n    while (lua_next(L, index) != 0) { /* stack: table key value */\n        dd(\"key type: %s\", luaL_typename(L, -2));\n\n        if (lua_type(L, -2) == LUA_TNUMBER) {\n\n            key = lua_tonumber(L, -2);\n\n            dd(\"key value: %d\", (int) key);\n\n            if (floor(key) == key && key >= 1) {\n                if (key > max) {\n                    max = (int) key;\n                }\n\n                lua_pop(L, 1); /* stack: table key */\n                continue;\n            }\n        }\n\n        /* not an array (non positive integer key) */\n        lua_pop(L, 2); /* stack: table */\n\n        luaL_argerror(L, arg_i, \"non-array table found\");\n        return 0;\n    }\n\n    size = 0;\n\n    for (i = 1; i <= max; i++) {\n        lua_rawgeti(L, index, i); /* stack: table value */\n        type = lua_type(L, -1);\n\n        switch (type) {\n            case LUA_TNUMBER:\n                size += ngx_http_lua_get_num_len(L, -1);\n                break;\n\n            case LUA_TSTRING:\n                lua_tolstring(L, -1, &len);\n                size += len;\n                break;\n\n            case LUA_TNIL:\n\n                if (strict) {\n                    goto bad_type;\n                }\n\n                size += sizeof(\"nil\") - 1;\n                break;\n\n            case LUA_TBOOLEAN:\n\n                if (strict) {\n                    goto bad_type;\n                }\n\n                if (lua_toboolean(L, -1)) {\n                    size += sizeof(\"true\") - 1;\n\n                } else {\n                    size += sizeof(\"false\") - 1;\n                }\n\n                break;\n\n            case LUA_TTABLE:\n\n                size += ngx_http_lua_calc_strlen_in_table(L, -1, arg_i, strict);\n                break;\n\n            case LUA_TLIGHTUSERDATA:\n\n                if (strict) {\n                    goto bad_type;\n                }\n\n                if (lua_touserdata(L, -1) == NULL) {\n                    size += sizeof(\"null\") - 1;\n                    break;\n                }\n\n                continue;\n\n            default:\n\nbad_type:\n\n                msg = lua_pushfstring(L, \"bad data type %s found\",\n                                      lua_typename(L, type));\n                return luaL_argerror(L, arg_i, msg);\n        }\n\n        lua_pop(L, 1); /* stack: table */\n    }\n\n    return size;\n}\n\n\nu_char *\nngx_http_lua_copy_str_in_table(lua_State *L, int index, u_char *dst)\n{\n    double               key;\n    int                  max;\n    int                  i;\n    int                  type;\n    size_t               len;\n    u_char              *p;\n\n    if (index < 0) {\n        index = lua_gettop(L) + index + 1;\n    }\n\n    max = 0;\n\n    lua_pushnil(L); /* stack: table key */\n    while (lua_next(L, index) != 0) { /* stack: table key value */\n        key = lua_tonumber(L, -2);\n        if (key > max) {\n            max = (int) key;\n        }\n\n        lua_pop(L, 1); /* stack: table key */\n    }\n\n    for (i = 1; i <= max; i++) {\n        lua_rawgeti(L, index, i); /* stack: table value */\n        type = lua_type(L, -1);\n        switch (type) {\n            case LUA_TNUMBER:\n                dst = ngx_http_lua_write_num(L, -1, dst);\n                break;\n\n            case LUA_TSTRING:\n                p = (u_char *) lua_tolstring(L, -1, &len);\n                dst = ngx_copy(dst, p, len);\n                break;\n\n            case LUA_TNIL:\n                *dst++ = 'n';\n                *dst++ = 'i';\n                *dst++ = 'l';\n                break;\n\n            case LUA_TBOOLEAN:\n                if (lua_toboolean(L, -1)) {\n                    *dst++ = 't';\n                    *dst++ = 'r';\n                    *dst++ = 'u';\n                    *dst++ = 'e';\n\n                } else {\n                    *dst++ = 'f';\n                    *dst++ = 'a';\n                    *dst++ = 'l';\n                    *dst++ = 's';\n                    *dst++ = 'e';\n                }\n\n                break;\n\n            case LUA_TTABLE:\n                dst = ngx_http_lua_copy_str_in_table(L, -1, dst);\n                break;\n\n            case LUA_TLIGHTUSERDATA:\n\n                *dst++ = 'n';\n                *dst++ = 'u';\n                *dst++ = 'l';\n                *dst++ = 'l';\n                break;\n\n            default:\n                luaL_error(L, \"impossible to reach here\");\n                return NULL;\n        }\n\n        lua_pop(L, 1); /* stack: table */\n    }\n\n    return dst;\n}\n\n\n/**\n * Force flush out response content\n * */\nstatic int\nngx_http_lua_ngx_flush(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_chain_t                 *cl;\n    ngx_int_t                    rc;\n    int                          n;\n    unsigned                     wait = 0;\n    ngx_event_t                 *wev;\n    ngx_http_core_loc_conf_t    *clcf;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    n = lua_gettop(L);\n    if (n > 1) {\n        return luaL_error(L, \"attempt to pass %d arguments, but accepted 0 \"\n                          \"or 1\", n);\n    }\n\n    r = ngx_http_lua_get_req(L);\n\n    if (n == 1 && r == r->main) {\n        luaL_checktype(L, 1, LUA_TBOOLEAN);\n        wait = lua_toboolean(L, 1);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_ACCESS\n                               | NGX_HTTP_LUA_CONTEXT_PRECONTENT\n                               | NGX_HTTP_LUA_CONTEXT_CONTENT);\n\n    if (ctx->acquired_raw_req_socket) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"raw request socket acquired\");\n        return 2;\n    }\n\n    coctx = ctx->cur_co_ctx;\n    if (coctx == NULL) {\n        return luaL_error(L, \"no co ctx found\");\n    }\n\n    if (r->header_only) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"header only\");\n        return 2;\n    }\n\n    if (ctx->eof) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"seen eof\");\n        return 2;\n    }\n\n    if (ctx->buffering) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua http 1.0 buffering makes ngx.flush() a no-op\");\n\n        lua_pushnil(L);\n        lua_pushliteral(L, \"buffering\");\n        return 2;\n    }\n\n#if 1\n    if ((!r->header_sent && !ctx->header_sent)\n        || (!ctx->seen_body_data && !wait))\n    {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"nothing to flush\");\n        return 2;\n    }\n#endif\n\n    cl = ngx_http_lua_get_flush_chain(r, ctx);\n    if (cl == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    rc = ngx_http_lua_send_chain_link(r, ctx, cl);\n\n    dd(\"send chain: %d\", (int) rc);\n\n    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"nginx output filter error\");\n        return 2;\n    }\n\n    dd(\"wait:%d, rc:%d, buffered:0x%x\", wait, (int) rc,\n       r->connection->buffered);\n\n    wev = r->connection->write;\n\n    if (wait && (r->connection->buffered\n                 & (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED)\n                 || wev->delayed))\n    {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua flush requires waiting: buffered 0x%uxd, \"\n                       \"delayed:%d\", (unsigned) r->connection->buffered,\n                       wev->delayed);\n\n        coctx->flushing = 1;\n        ctx->flushing_coros++;\n\n        if (ctx->entered_content_phase) {\n            /* mimic ngx_http_set_write_handler */\n            r->write_event_handler = ngx_http_lua_content_wev_handler;\n\n        } else {\n            r->write_event_handler = ngx_http_core_run_phases;\n        }\n\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n        if (!wev->delayed) {\n            ngx_add_timer(wev, clcf->send_timeout);\n        }\n\n        if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {\n            if (wev->timer_set) {\n                wev->delayed = 0;\n                ngx_del_timer(wev);\n            }\n\n            lua_pushnil(L);\n            lua_pushliteral(L, \"connection broken\");\n            return 2;\n        }\n\n        ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);\n        coctx->cleanup = ngx_http_lua_flush_cleanup;\n        coctx->data = r;\n\n        return lua_yield(L, 0);\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua flush asynchronously\");\n\n    lua_pushinteger(L, 1);\n    return 1;\n}\n\n\n/**\n * Send last_buf, terminate output stream\n * */\nstatic int\nngx_http_lua_ngx_eof(lua_State *L)\n{\n    ngx_http_request_t      *r;\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_int_t                rc;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request object found\");\n    }\n\n    if (lua_gettop(L) != 0) {\n        return luaL_error(L, \"no argument is expected\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    if (ctx->acquired_raw_req_socket) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"raw request socket acquired\");\n        return 2;\n    }\n\n    if (ctx->eof) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"seen eof\");\n        return 2;\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_ACCESS\n                               | NGX_HTTP_LUA_CONTEXT_PRECONTENT\n                               | NGX_HTTP_LUA_CONTEXT_CONTENT);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua send eof\");\n\n    rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */);\n\n    dd(\"send chain: %d\", (int) rc);\n\n    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"nginx output filter error\");\n        return 2;\n    }\n\n    lua_pushinteger(L, 1);\n    return 1;\n}\n\n\nvoid\nngx_http_lua_inject_output_api(lua_State *L)\n{\n    lua_pushcfunction(L, ngx_http_lua_ngx_send_headers);\n    lua_setfield(L, -2, \"send_headers\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_print);\n    lua_setfield(L, -2, \"print\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_say);\n    lua_setfield(L, -2, \"say\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_flush);\n    lua_setfield(L, -2, \"flush\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_eof);\n    lua_setfield(L, -2, \"eof\");\n}\n\n\n/**\n * Send out headers\n * */\nstatic int\nngx_http_lua_ngx_send_headers(lua_State *L)\n{\n    ngx_int_t                rc;\n    ngx_http_request_t      *r;\n    ngx_http_lua_ctx_t      *ctx;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_ACCESS\n                               | NGX_HTTP_LUA_CONTEXT_PRECONTENT\n                               | NGX_HTTP_LUA_CONTEXT_CONTENT);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua send headers\");\n\n    rc = ngx_http_lua_send_header_if_needed(r, ctx);\n    if (rc == NGX_ERROR || rc > NGX_OK) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"nginx output filter error\");\n        return 2;\n    }\n\n    lua_pushinteger(L, 1);\n    return 1;\n}\n\n\nngx_int_t\nngx_http_lua_flush_resume_helper(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx)\n{\n    int                          n;\n    lua_State                   *vm;\n    ngx_int_t                    rc;\n    ngx_uint_t                   nreqs;\n    ngx_connection_t            *c;\n\n    c = r->connection;\n\n    ctx->cur_co_ctx->cleanup = NULL;\n\n    /* push the return values */\n\n    if (c->timedout) {\n        lua_pushnil(ctx->cur_co_ctx->co);\n        lua_pushliteral(ctx->cur_co_ctx->co, \"timeout\");\n        n = 2;\n\n    } else if (c->error) {\n        lua_pushnil(ctx->cur_co_ctx->co);\n        lua_pushliteral(ctx->cur_co_ctx->co, \"client aborted\");\n        n = 2;\n\n    } else {\n        lua_pushinteger(ctx->cur_co_ctx->co, 1);\n        n = 1;\n    }\n\n    vm = ngx_http_lua_get_lua_vm(r, ctx);\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(vm, r, ctx, n);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run thread returned %d\", rc);\n\n    if (rc == NGX_AGAIN) {\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    /* rc == NGX_ERROR || rc >= NGX_OK */\n\n    if (ctx->entered_content_phase) {\n        ngx_http_lua_finalize_request(r, rc);\n        return NGX_DONE;\n    }\n\n    return rc;\n}\n\n\nstatic void\nngx_http_lua_flush_cleanup(void *data)\n{\n    ngx_http_request_t                      *r;\n    ngx_event_t                             *wev;\n    ngx_http_lua_ctx_t                      *ctx;\n    ngx_http_lua_co_ctx_t                   *coctx = data;\n\n    coctx->flushing = 0;\n\n    r = coctx->data;\n    if (r == NULL) {\n        return;\n    }\n\n    wev = r->connection->write;\n\n    if (wev && wev->timer_set) {\n        ngx_del_timer(wev);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return;\n    }\n\n    ctx->flushing_coros--;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_output.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_OUTPUT_H_INCLUDED_\n#define _NGX_HTTP_LUA_OUTPUT_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_output_api(lua_State *L);\n\nsize_t ngx_http_lua_calc_strlen_in_table(lua_State *L, int index, int arg_i,\n    unsigned strict);\n\nu_char *ngx_http_lua_copy_str_in_table(lua_State *L, int index, u_char *dst);\n\nngx_int_t ngx_http_lua_flush_resume_helper(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx);\n\n\n/* Get the maximum possible length, not the actual length */\nstatic ngx_inline size_t\nngx_http_lua_get_num_len(lua_State *L, int idx)\n{\n    double     num;\n\n    num = (double) lua_tonumber(L, idx);\n    if (num == (double) (int32_t) num) {\n        return NGX_INT32_LEN;\n    }\n\n    return NGX_DOUBLE_LEN;\n}\n\n\nstatic ngx_inline u_char *\nngx_http_lua_write_num(lua_State *L, int idx, u_char *dst)\n{\n    double     num;\n    int        n;\n\n    num = (double) lua_tonumber(L, idx);\n    /*\n     * luajit format number with only 14 significant digits.\n     * To be consistent with lujit, don't use (double) (long) below\n     * or integer greater than 99,999,999,999,999 will different from luajit.\n     */\n    if (num == (double) (int32_t) num) {\n        dst = ngx_snprintf(dst, NGX_INT64_LEN, \"%D\", (int32_t) num);\n\n    } else {\n        /*\n         * The maximum number of significant digits is 14 in lua.\n         * Please refer to lj_strfmt.c for more details.\n         */\n        n = snprintf((char *) dst, NGX_DOUBLE_LEN, \"%.14g\", num);\n        if (n < 0) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                          \"snprintf(\\\"%f\\\") failed\");\n\n        } else {\n            dst += n;\n        }\n    }\n\n    return dst;\n}\n\n\n#endif /* _NGX_HTTP_LUA_OUTPUT_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_pcrefix.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_pcrefix.h\"\n#include \"stdio.h\"\n\n#if (NGX_PCRE)\n\nstatic ngx_pool_t *ngx_http_lua_pcre_pool = NULL;\n\n\n#if (NGX_PCRE2)\nstatic ngx_uint_t  ngx_regex_direct_alloc;\n#else\nstatic void *(*old_pcre_malloc)(size_t);\nstatic void (*old_pcre_free)(void *ptr);\n#endif\n\n\n/* XXX: work-around to nginx regex subsystem, must init a memory pool\n * to use PCRE functions. As PCRE still has memory-leaking problems,\n * and nginx overwrote pcre_malloc/free hooks with its own static\n * functions, so nobody else can reuse nginx regex subsystem... */\n#if (NGX_PCRE2)\n\nvoid *\nngx_http_lua_pcre_malloc(size_t size, void *data)\n{\n    dd(\"lua pcre pool is %p\", ngx_http_lua_pcre_pool);\n\n    if (ngx_http_lua_pcre_pool) {\n        return ngx_palloc(ngx_http_lua_pcre_pool, size);\n    }\n\n    if (ngx_regex_direct_alloc) {\n        return ngx_alloc(size, ngx_cycle->log);\n    }\n\n    fprintf(stderr, \"error: lua pcre malloc failed due to empty pcre pool\");\n\n    return NULL;\n}\n\n\nvoid\nngx_http_lua_pcre_free(void *ptr, void *data)\n{\n    dd(\"lua pcre pool is %p\", ngx_http_lua_pcre_pool);\n\n    if (ngx_http_lua_pcre_pool) {\n        ngx_pfree(ngx_http_lua_pcre_pool, ptr);\n        return;\n    }\n\n    if (ngx_regex_direct_alloc) {\n        ngx_free(ptr);\n        return;\n    }\n\n    fprintf(stderr, \"error: lua pcre free failed due to empty pcre pool\");\n}\n\n#else\n\nvoid *\nngx_http_lua_pcre_malloc(size_t size)\n{\n    dd(\"lua pcre pool is %p\", ngx_http_lua_pcre_pool);\n\n    if (ngx_http_lua_pcre_pool) {\n        return ngx_palloc(ngx_http_lua_pcre_pool, size);\n    }\n\n    fprintf(stderr, \"error: lua pcre malloc failed due to empty pcre pool\");\n\n    return NULL;\n}\n\n\nstatic void\nngx_http_lua_pcre_free(void *ptr)\n{\n    dd(\"lua pcre pool is %p\", ngx_http_lua_pcre_pool);\n\n    if (ngx_http_lua_pcre_pool) {\n        ngx_pfree(ngx_http_lua_pcre_pool, ptr);\n        return;\n    }\n\n    fprintf(stderr, \"error: lua pcre free failed due to empty pcre pool\");\n}\n\n#endif\n\n\n#if (NGX_PCRE2)\n\nngx_pool_t *\nngx_http_lua_pcre_malloc_init(ngx_pool_t *pool)\n{\n    ngx_pool_t          *old_pool;\n\n    dd(\"lua pcre pool was %p\", ngx_http_lua_pcre_pool);\n\n    ngx_regex_direct_alloc = (pool == NULL) ? 1 : 0;\n\n    old_pool = ngx_http_lua_pcre_pool;\n    ngx_http_lua_pcre_pool = pool;\n\n    dd(\"lua pcre pool is %p\", ngx_http_lua_pcre_pool);\n\n    return old_pool;\n}\n\n\nvoid\nngx_http_lua_pcre_malloc_done(ngx_pool_t *old_pool)\n{\n    dd(\"lua pcre pool was %p\", ngx_http_lua_pcre_pool);\n\n    ngx_http_lua_pcre_pool = old_pool;\n    ngx_regex_direct_alloc = 0;\n\n    dd(\"lua pcre pool is %p\", ngx_http_lua_pcre_pool);\n}\n\n#else\n\nngx_pool_t *\nngx_http_lua_pcre_malloc_init(ngx_pool_t *pool)\n{\n    ngx_pool_t          *old_pool;\n\n    if (pcre_malloc != ngx_http_lua_pcre_malloc) {\n\n        dd(\"overriding nginx pcre malloc and free\");\n\n        ngx_http_lua_pcre_pool = pool;\n\n        old_pcre_malloc = pcre_malloc;\n        old_pcre_free = pcre_free;\n\n        pcre_malloc = ngx_http_lua_pcre_malloc;\n        pcre_free = ngx_http_lua_pcre_free;\n\n        return NULL;\n    }\n\n    dd(\"lua pcre pool was %p\", ngx_http_lua_pcre_pool);\n\n    old_pool = ngx_http_lua_pcre_pool;\n    ngx_http_lua_pcre_pool = pool;\n\n    dd(\"lua pcre pool is %p\", ngx_http_lua_pcre_pool);\n\n    return old_pool;\n}\n\n\nvoid\nngx_http_lua_pcre_malloc_done(ngx_pool_t *old_pool)\n{\n    dd(\"lua pcre pool was %p\", ngx_http_lua_pcre_pool);\n\n    ngx_http_lua_pcre_pool = old_pool;\n\n    dd(\"lua pcre pool is %p\", ngx_http_lua_pcre_pool);\n\n    if (old_pool == NULL) {\n        pcre_malloc = old_pcre_malloc;\n        pcre_free = old_pcre_free;\n    }\n}\n\n#endif\n#endif /* NGX_PCRE */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_pcrefix.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_PCREFIX_H_INCLUDED_\n#define _NGX_HTTP_LUA_PCREFIX_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\n#if (NGX_PCRE)\n\nngx_pool_t *ngx_http_lua_pcre_malloc_init(ngx_pool_t *pool);\nvoid ngx_http_lua_pcre_malloc_done(ngx_pool_t *old_pool);\n\n#if NGX_PCRE2\nvoid *ngx_http_lua_pcre_malloc(size_t size, void *data);\nvoid ngx_http_lua_pcre_free(void *ptr, void *data);\n#endif\n\n#endif\n\n\n#endif /* _NGX_HTTP_LUA_PCREFIX_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_phase.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nint\nngx_http_lua_ffi_get_phase(ngx_http_request_t *r, char **err)\n{\n    ngx_http_lua_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        *err = \"no request context\";\n        return NGX_ERROR;\n    }\n\n    return ctx->context;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_pipe.c",
    "content": "\n/*\n * Copyright (C) by OpenResty Inc.\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_common.h\"\n#include \"ngx_http_lua_input_filters.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_pipe.h\"\n#if (NGX_HTTP_LUA_HAVE_SIGNALFD)\n#include <sys/signalfd.h>\n#endif\n\n\n#ifdef HAVE_NGX_LUA_PIPE\nstatic ngx_rbtree_node_t *ngx_http_lua_pipe_lookup_pid(ngx_rbtree_key_t key);\n#if !(NGX_HTTP_LUA_HAVE_SIGNALFD)\nstatic void ngx_http_lua_pipe_sigchld_handler(int signo, siginfo_t *siginfo,\n    void *ucontext);\n#endif\nstatic void ngx_http_lua_pipe_sigchld_event_handler(ngx_event_t *ev);\nstatic ssize_t ngx_http_lua_pipe_fd_read(ngx_connection_t *c, u_char *buf,\n    size_t size);\nstatic ssize_t ngx_http_lua_pipe_fd_write(ngx_connection_t *c, u_char *buf,\n    size_t size);\nstatic void ngx_http_lua_pipe_close_helper(ngx_http_lua_pipe_t *pipe,\n    ngx_http_lua_pipe_ctx_t *pipe_ctx, ngx_event_t *ev);\nstatic void ngx_http_lua_pipe_close_stdin(ngx_http_lua_pipe_t *pipe);\nstatic void ngx_http_lua_pipe_close_stdout(ngx_http_lua_pipe_t *pipe);\nstatic void ngx_http_lua_pipe_close_stderr(ngx_http_lua_pipe_t *pipe);\nstatic void ngx_http_lua_pipe_proc_finalize(ngx_http_lua_ffi_pipe_proc_t *proc);\nstatic ngx_int_t ngx_http_lua_pipe_get_lua_ctx(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t **ctx, u_char *errbuf, size_t *errbuf_size);\nstatic void ngx_http_lua_pipe_put_error(ngx_http_lua_pipe_ctx_t *pipe_ctx,\n    u_char *errbuf, size_t *errbuf_size);\nstatic void ngx_http_lua_pipe_put_data(ngx_http_lua_pipe_t *pipe,\n    ngx_http_lua_pipe_ctx_t *pipe_ctx, u_char **buf, size_t *buf_size);\nstatic ngx_int_t ngx_http_lua_pipe_add_input_buffer(ngx_http_lua_pipe_t *pipe,\n    ngx_http_lua_pipe_ctx_t *pipe_ctx);\nstatic ngx_int_t ngx_http_lua_pipe_read_all(void *data, ssize_t bytes);\nstatic ngx_int_t ngx_http_lua_pipe_read_bytes(void *data, ssize_t bytes);\nstatic ngx_int_t ngx_http_lua_pipe_read_line(void *data, ssize_t bytes);\nstatic ngx_int_t ngx_http_lua_pipe_read_any(void *data, ssize_t bytes);\nstatic ngx_int_t ngx_http_lua_pipe_read(ngx_http_lua_pipe_t *pipe,\n    ngx_http_lua_pipe_ctx_t *pipe_ctx);\nstatic ngx_int_t ngx_http_lua_pipe_init_ctx(\n    ngx_http_lua_pipe_ctx_t **pipe_ctx_pt, int fd, ngx_pool_t *pool,\n    u_char *errbuf, size_t *errbuf_size);\nstatic ngx_int_t ngx_http_lua_pipe_write(ngx_http_lua_pipe_t *pipe,\n    ngx_http_lua_pipe_ctx_t *pipe_ctx);\nstatic int ngx_http_lua_pipe_read_stdout_retval(\n    ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L);\nstatic int ngx_http_lua_pipe_read_stderr_retval(\n    ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L);\nstatic int ngx_http_lua_pipe_read_retval_helper(\n    ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L, int from_stderr);\nstatic int ngx_http_lua_pipe_write_retval(ngx_http_lua_ffi_pipe_proc_t *proc,\n    lua_State *L);\nstatic int ngx_http_lua_pipe_wait_retval(ngx_http_lua_ffi_pipe_proc_t *proc,\n    lua_State *L);\nstatic void ngx_http_lua_pipe_resume_helper(ngx_event_t *ev,\n    ngx_http_lua_co_ctx_t *wait_co_ctx);\nstatic void ngx_http_lua_pipe_resume_read_stdout_handler(ngx_event_t *ev);\nstatic void ngx_http_lua_pipe_resume_read_stderr_handler(ngx_event_t *ev);\nstatic void ngx_http_lua_pipe_resume_write_handler(ngx_event_t *ev);\nstatic void ngx_http_lua_pipe_resume_wait_handler(ngx_event_t *ev);\nstatic ngx_int_t ngx_http_lua_pipe_resume(ngx_http_request_t *r);\nstatic void ngx_http_lua_pipe_dummy_event_handler(ngx_event_t *ev);\nstatic void ngx_http_lua_pipe_clear_event(ngx_event_t *ev);\nstatic void ngx_http_lua_pipe_proc_read_stdout_cleanup(void *data);\nstatic void ngx_http_lua_pipe_proc_read_stderr_cleanup(void *data);\nstatic void ngx_http_lua_pipe_proc_write_cleanup(void *data);\nstatic void ngx_http_lua_pipe_proc_wait_cleanup(void *data);\nstatic void ngx_http_lua_pipe_reap_pids(ngx_event_t *ev);\nstatic void ngx_http_lua_pipe_reap_timer_handler(ngx_event_t *ev);\nvoid ngx_http_lua_ffi_pipe_proc_destroy(\n    ngx_http_lua_ffi_pipe_proc_t *proc);\n\n\nstatic ngx_rbtree_t       ngx_http_lua_pipe_rbtree;\nstatic ngx_rbtree_node_t  ngx_http_lua_pipe_proc_sentinel;\nstatic ngx_event_t        ngx_reap_pid_event;\n\n\n#if (NGX_HTTP_LUA_HAVE_SIGNALFD)\nstatic int                                ngx_http_lua_signalfd;\nstatic struct signalfd_siginfo            ngx_http_lua_pipe_notification;\n\n#define ngx_http_lua_read_sigfd           ngx_http_lua_signalfd\n\n#else\nstatic int                                ngx_http_lua_sigchldfd[2];\nstatic u_char                             ngx_http_lua_pipe_notification[1];\n\n#define ngx_http_lua_read_sigfd           ngx_http_lua_sigchldfd[0]\n#define ngx_http_lua_write_sigfd          ngx_http_lua_sigchldfd[1]\n#endif\n\n\nstatic ngx_connection_t                  *ngx_http_lua_sigfd_conn = NULL;\n\n\n/* The below signals are ignored by Nginx.\n * We need to reset them for the spawned child processes. */\nngx_http_lua_pipe_signal_t ngx_signals[] = {\n    { SIGSYS, \"SIGSYS\" },\n    { SIGPIPE, \"SIGPIPE\" },\n    { 0, NULL }\n};\n\n\nenum {\n    PIPE_ERR_CLOSED = 1,\n    PIPE_ERR_SYSCALL,\n    PIPE_ERR_NOMEM,\n    PIPE_ERR_TIMEOUT,\n    PIPE_ERR_ADD_READ_EV,\n    PIPE_ERR_ADD_WRITE_EV,\n    PIPE_ERR_ABORTED,\n};\n\n\nenum {\n    PIPE_READ_ALL = 0,\n    PIPE_READ_BYTES,\n    PIPE_READ_LINE,\n    PIPE_READ_ANY,\n};\n\n\n#define REASON_EXIT         \"exit\"\n#define REASON_SIGNAL       \"signal\"\n#define REASON_UNKNOWN      \"unknown\"\n\n#define REASON_RUNNING_CODE  0\n#define REASON_EXIT_CODE     1\n#define REASON_SIGNAL_CODE   2\n#define REASON_UNKNOWN_CODE  3\n\n\nvoid\nngx_http_lua_pipe_init(void)\n{\n    ngx_rbtree_init(&ngx_http_lua_pipe_rbtree,\n                    &ngx_http_lua_pipe_proc_sentinel, ngx_rbtree_insert_value);\n}\n\n\nngx_int_t\nngx_http_lua_pipe_add_signal_handler(ngx_cycle_t *cycle)\n{\n    ngx_event_t         *rev;\n#if (NGX_HTTP_LUA_HAVE_SIGNALFD)\n    sigset_t             set;\n\n#else\n    int                  rc;\n    struct sigaction     sa;\n#endif\n\n    ngx_reap_pid_event.handler = ngx_http_lua_pipe_reap_timer_handler;\n    ngx_reap_pid_event.log = cycle->log;\n    ngx_reap_pid_event.data = cycle;\n    ngx_reap_pid_event.cancelable = 1;\n\n    if (!ngx_reap_pid_event.timer_set) {\n        ngx_add_timer(&ngx_reap_pid_event, 1000);\n    }\n\n#if (NGX_HTTP_LUA_HAVE_SIGNALFD)\n    if (sigemptyset(&set) != 0) {\n        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,\n                      \"lua pipe init signal set failed\");\n        return NGX_ERROR;\n    }\n\n    if (sigaddset(&set, SIGCHLD) != 0) {\n        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,\n                      \"lua pipe add SIGCHLD to signal set failed\");\n        return NGX_ERROR;\n    }\n\n    if (sigprocmask(SIG_BLOCK, &set, NULL) != 0) {\n        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,\n                      \"lua pipe block SIGCHLD failed\");\n        return NGX_ERROR;\n    }\n\n    ngx_http_lua_signalfd = signalfd(-1, &set, SFD_NONBLOCK|SFD_CLOEXEC);\n    if (ngx_http_lua_signalfd < 0) {\n        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,\n                      \"lua pipe create signalfd instance failed\");\n        return NGX_ERROR;\n    }\n\n#else /* !(NGX_HTTP_LUA_HAVE_SIGNALFD) */\n#   if (NGX_HTTP_LUA_HAVE_PIPE2)\n    rc = pipe2(ngx_http_lua_sigchldfd, O_NONBLOCK|O_CLOEXEC);\n#   else\n    rc = pipe(ngx_http_lua_sigchldfd);\n#   endif\n\n    if (rc == -1) {\n        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,\n                      \"lua pipe init SIGCHLD fd failed\");\n        return NGX_ERROR;\n    }\n\n#   if !(NGX_HTTP_LUA_HAVE_PIPE2)\n    if (ngx_nonblocking(ngx_http_lua_read_sigfd) == -1) {\n        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, \"lua pipe \"\n                      ngx_nonblocking_n \" SIGCHLD read fd failed\");\n        goto failed;\n    }\n\n    if (ngx_nonblocking(ngx_http_lua_write_sigfd) == -1) {\n        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, \"lua pipe \"\n                      ngx_nonblocking_n \" SIGCHLD write fd failed\");\n        goto failed;\n    }\n\n    /* it's ok not to set the pipe fd with O_CLOEXEC. This requires\n     * extra syscall */\n#   endif /* !(NGX_HTTP_LUA_HAVE_PIPE2) */\n#endif /* NGX_HTTP_LUA_HAVE_SIGNALFD */\n\n    ngx_http_lua_sigfd_conn = ngx_get_connection(ngx_http_lua_read_sigfd,\n                                                 cycle->log);\n    if (ngx_http_lua_sigfd_conn == NULL) {\n        goto failed;\n    }\n\n    ngx_http_lua_sigfd_conn->log = cycle->log;\n    ngx_http_lua_sigfd_conn->recv = ngx_http_lua_pipe_fd_read;\n    rev = ngx_http_lua_sigfd_conn->read;\n    rev->log = ngx_http_lua_sigfd_conn->log;\n    rev->handler = ngx_http_lua_pipe_sigchld_event_handler;\n\n#ifdef HAVE_SOCKET_CLOEXEC_PATCH\n    rev->skip_socket_leak_check = 1;\n#endif\n\n    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {\n        goto failed;\n    }\n\n#if !(NGX_HTTP_LUA_HAVE_SIGNALFD)\n    ngx_memzero(&sa, sizeof(struct sigaction));\n    sa.sa_sigaction = ngx_http_lua_pipe_sigchld_handler;\n    sa.sa_flags = SA_SIGINFO;\n\n    if (sigemptyset(&sa.sa_mask) != 0) {\n        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,\n                      \"lua pipe init signal mask failed\");\n        goto failed;\n    }\n\n    if (sigaction(SIGCHLD, &sa, NULL) == -1) {\n        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,\n                      \"lua pipe sigaction(SIGCHLD) failed\");\n        goto failed;\n    }\n#endif\n\n    return NGX_OK;\n\nfailed:\n\n    if (ngx_http_lua_sigfd_conn != NULL) {\n        ngx_close_connection(ngx_http_lua_sigfd_conn);\n        ngx_http_lua_sigfd_conn = NULL;\n    }\n\n    if (close(ngx_http_lua_read_sigfd) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                      \"lua pipe close the read sigfd failed\");\n    }\n\n#if !(NGX_HTTP_LUA_HAVE_SIGNALFD)\n    if (close(ngx_http_lua_write_sigfd) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                      \"lua pipe close the write sigfd failed\");\n    }\n#endif\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_rbtree_node_t *\nngx_http_lua_pipe_lookup_pid(ngx_rbtree_key_t key)\n{\n    ngx_rbtree_node_t    *node, *sentinel;\n\n    node = ngx_http_lua_pipe_rbtree.root;\n    sentinel = ngx_http_lua_pipe_rbtree.sentinel;\n\n    while (node != sentinel) {\n        if (key < node->key) {\n            node = node->left;\n            continue;\n        }\n\n        if (key > node->key) {\n            node = node->right;\n            continue;\n        }\n\n        return node;\n    }\n\n    return NULL;\n}\n\n\n#if !(NGX_HTTP_LUA_HAVE_SIGNALFD)\nstatic void\nngx_http_lua_pipe_sigchld_handler(int signo, siginfo_t *siginfo,\n    void *ucontext)\n{\n    ngx_err_t                        err, saved_err;\n    ngx_int_t                        n;\n\n    saved_err = ngx_errno;\n\n    for ( ;; ) {\n        n = write(ngx_http_lua_write_sigfd, ngx_http_lua_pipe_notification,\n                  sizeof(ngx_http_lua_pipe_notification));\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,\n                       \"lua pipe SIGCHLD fd write siginfo:%p\", siginfo);\n\n        if (n >= 0) {\n            break;\n        }\n\n        err = ngx_errno;\n\n        if (err != NGX_EINTR) {\n            if (err != NGX_EAGAIN) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, err,\n                              \"lua pipe SIGCHLD fd write failed\");\n            }\n\n            break;\n        }\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, err,\n                       \"lua pipe SIGCHLD fd write was interrupted\");\n    }\n\n    ngx_set_errno(saved_err);\n}\n#endif\n\n\nstatic void\nngx_http_lua_pipe_sigchld_event_handler(ngx_event_t *ev)\n{\n    int                              n;\n    ngx_connection_t                *c = ev->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,\n                   \"lua pipe reaping children\");\n\n    for ( ;; ) {\n#if (NGX_HTTP_LUA_HAVE_SIGNALFD)\n        n = c->recv(c, (u_char *) &ngx_http_lua_pipe_notification,\n#else\n        n = c->recv(c, ngx_http_lua_pipe_notification,\n#endif\n                    sizeof(ngx_http_lua_pipe_notification));\n\n        if (n <= 0) {\n            if (n == NGX_ERROR) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                              \"lua pipe SIGCHLD fd read failed\");\n            }\n\n            break;\n        }\n\n        ngx_http_lua_pipe_reap_pids(ev);\n    }\n}\n\n\nstatic void\nngx_http_lua_pipe_reap_pids(ngx_event_t *ev)\n{\n    int                              status;\n    ngx_pid_t                        pid;\n    ngx_rbtree_node_t               *node;\n    ngx_http_lua_pipe_node_t        *pipe_node;\n\n    for ( ;; ) {\n        pid = waitpid(-1, &status, WNOHANG);\n\n        if (pid == 0) {\n            break;\n        }\n\n        if (pid < 0) {\n            if (ngx_errno != NGX_ECHILD) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                              \"lua pipe waitpid failed\");\n            }\n\n            break;\n        }\n\n        /* This log is ported from Nginx's signal handler since we override\n         * or block it in this implementation. */\n        ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,\n                      \"signal %d (SIGCHLD) received from %P\",\n                      SIGCHLD, pid);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua pipe SIGCHLD fd read pid:%P status:%d\", pid,\n                       status);\n\n        node = ngx_http_lua_pipe_lookup_pid(pid);\n        if (node != NULL) {\n            pipe_node = (ngx_http_lua_pipe_node_t *) &node->color;\n            if (pipe_node->wait_co_ctx != NULL) {\n                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                               \"lua pipe resume process:%p waiting for %P\",\n                               pipe_node->proc, pid);\n\n                /*\n                 * We need the extra parentheses around the first argument\n                 * of ngx_post_event() just to work around macro issues in\n                 * nginx cores older than 1.7.12 (exclusive).\n                 */\n                ngx_post_event((&pipe_node->wait_co_ctx->sleep),\n                               &ngx_posted_events);\n            }\n\n            /* TODO: we should proactively close and free up the pipe after\n             * the user consume all the data in the pipe.\n             */\n            pipe_node->proc->pipe->dead = 1;\n\n            if (WIFSIGNALED(status)) {\n                pipe_node->status = WTERMSIG(status);\n                pipe_node->reason_code = REASON_SIGNAL_CODE;\n\n            } else if (WIFEXITED(status)) {\n                pipe_node->status = WEXITSTATUS(status);\n                pipe_node->reason_code = REASON_EXIT_CODE;\n\n            } else {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                              \"lua pipe unknown exit status %d from \"\n                              \"process %P\", status, pid);\n                pipe_node->status = status;\n                pipe_node->reason_code = REASON_UNKNOWN_CODE;\n            }\n        }\n    }\n}\n\n\nstatic void\nngx_http_lua_pipe_reap_timer_handler(ngx_event_t *ev)\n{\n    ngx_http_lua_pipe_reap_pids(ev);\n\n    if (!ngx_exiting) {\n        ngx_add_timer(&ngx_reap_pid_event, 1000);\n        ngx_reap_pid_event.timedout = 0;\n    }\n}\n\n\nstatic ssize_t\nngx_http_lua_pipe_fd_read(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    ssize_t       n;\n    ngx_err_t     err;\n    ngx_event_t  *rev;\n\n    rev = c->read;\n\n    do {\n        n = read(c->fd, buf, size);\n\n        err = ngx_errno;\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"read: fd:%d %z of %uz\", c->fd, n, size);\n\n        if (n == 0) {\n            rev->ready = 0;\n            rev->eof = 1;\n            return 0;\n        }\n\n        if (n > 0) {\n            if ((size_t) n < size\n                && !(ngx_event_flags & NGX_USE_GREEDY_EVENT))\n            {\n                rev->ready = 0;\n            }\n\n            return n;\n        }\n\n        if (err == NGX_EAGAIN || err == NGX_EINTR) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"read() not ready\");\n            n = NGX_AGAIN;\n\n        } else {\n            n = ngx_connection_error(c, err, \"read() failed\");\n            break;\n        }\n\n    } while (err == NGX_EINTR);\n\n    rev->ready = 0;\n\n    if (n == NGX_ERROR) {\n        rev->error = 1;\n    }\n\n    return n;\n}\n\n\nstatic ssize_t\nngx_http_lua_pipe_fd_write(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    ssize_t       n;\n    ngx_err_t     err;\n    ngx_event_t  *wev;\n\n    wev = c->write;\n\n    do {\n        n = write(c->fd, buf, size);\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"write: fd:%d %z of %uz\", c->fd, n, size);\n\n        if (n >= 0) {\n            if ((size_t) n != size) {\n                wev->ready = 0;\n            }\n\n            return n;\n        }\n\n        err = ngx_errno;\n\n        if (err == NGX_EAGAIN || err == NGX_EINTR) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"write() not ready\");\n            n = NGX_AGAIN;\n\n        } else if (err != NGX_EPIPE) {\n            n = ngx_connection_error(c, err, \"write() failed\");\n            break;\n        }\n\n    } while (err == NGX_EINTR);\n\n    wev->ready = 0;\n\n    if (n == NGX_ERROR) {\n        wev->error = 1;\n    }\n\n    return n;\n}\n\n\n#if !(NGX_HTTP_LUA_HAVE_EXECVPE)\nstatic int\nngx_http_lua_execvpe(const char *program, char * const argv[],\n    char * const envp[])\n{\n    int    rc;\n    char **saved = environ;\n\n    environ = (char **) envp;\n    rc = execvp(program, argv);\n    environ = saved;\n    return rc;\n}\n#endif\n\n\nint\nngx_http_lua_ffi_pipe_spawn(ngx_http_request_t *r,\n    ngx_http_lua_ffi_pipe_proc_t *proc,\n    const char *file, const char **argv, int merge_stderr, size_t buffer_size,\n    const char **environ, u_char *errbuf, size_t *errbuf_size)\n{\n    int                             rc;\n    int                             in[2];\n    int                             out[2];\n    int                             err[2];\n    int                             stdin_fd, stdout_fd, stderr_fd;\n    int                             errlog_fd, temp_errlog_fd;\n    ngx_pid_t                       pid;\n    ssize_t                         pool_size;\n    ngx_pool_t                     *pool;\n    ngx_uint_t                      i;\n    ngx_listening_t                *ls;\n    ngx_http_lua_pipe_t            *pp;\n    ngx_rbtree_node_t              *node;\n    ngx_http_lua_pipe_node_t       *pipe_node;\n    struct sigaction                sa;\n    ngx_http_lua_pipe_signal_t     *sig;\n    ngx_pool_cleanup_t             *cln;\n    sigset_t                        set;\n\n    pool_size = ngx_align(NGX_MIN_POOL_SIZE + buffer_size * 2,\n                          NGX_POOL_ALIGNMENT);\n\n    pool = ngx_create_pool(pool_size, ngx_cycle->log);\n    if (pool == NULL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"no memory\")\n                       - errbuf;\n        return NGX_ERROR;\n    }\n\n    pp = ngx_pcalloc(pool, sizeof(ngx_http_lua_pipe_t)\n                     + offsetof(ngx_rbtree_node_t, color)\n                     + sizeof(ngx_http_lua_pipe_node_t));\n    if (pp == NULL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"no memory\")\n                       - errbuf;\n        goto free_pool;\n    }\n\n    rc = pipe(in);\n    if (rc == -1) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"pipe failed: %s\",\n                                    strerror(errno))\n                       - errbuf;\n        goto free_pool;\n    }\n\n    rc = pipe(out);\n    if (rc == -1) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"pipe failed: %s\",\n                                    strerror(errno))\n                       - errbuf;\n        goto close_in_fd;\n    }\n\n    if (!merge_stderr) {\n        rc = pipe(err);\n        if (rc == -1) {\n            *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                        \"pipe failed: %s\", strerror(errno))\n                           - errbuf;\n            goto close_in_out_fd;\n        }\n    }\n\n    pid = fork();\n    if (pid == -1) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"fork failed: %s\",\n                                    strerror(errno))\n                       - errbuf;\n        goto close_in_out_err_fd;\n    }\n\n    if (pid == 0) {\n\n#if (NGX_HAVE_CPU_AFFINITY)\n        /* reset the CPU affinity mask */\n        ngx_uint_t     log_level;\n        ngx_cpuset_t   child_cpu_affinity;\n\n        if (ngx_process == NGX_PROCESS_WORKER\n            && ngx_get_cpu_affinity(ngx_worker) != NULL)\n        {\n            CPU_ZERO(&child_cpu_affinity);\n\n            for (i = 0; i < (ngx_uint_t) ngx_min(ngx_ncpu, CPU_SETSIZE); i++) {\n                CPU_SET(i, &child_cpu_affinity);\n            }\n\n            log_level = ngx_cycle->log->log_level;\n            ngx_cycle->log->log_level = NGX_LOG_WARN;\n            ngx_setaffinity(&child_cpu_affinity, ngx_cycle->log);\n            ngx_cycle->log->log_level = log_level;\n        }\n#endif\n\n        /* reset the handler of ignored signals to the default */\n        for (sig = ngx_signals; sig->signo != 0; sig++) {\n            ngx_memzero(&sa, sizeof(struct sigaction));\n            sa.sa_handler = SIG_DFL;\n\n            if (sigemptyset(&sa.sa_mask) != 0) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                              \"lua pipe child init signal mask failed\");\n                exit(EXIT_FAILURE);\n            }\n\n            if (sigaction(sig->signo, &sa, NULL) == -1) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                              \"lua pipe child reset signal handler for %s \"\n                              \"failed\", sig->signame);\n                exit(EXIT_FAILURE);\n            }\n        }\n\n        /* reset signal mask */\n        if (sigemptyset(&set) != 0) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                          \"lua pipe child init signal set failed\");\n            exit(EXIT_FAILURE);\n        }\n\n        if (sigprocmask(SIG_SETMASK, &set, NULL) != 0) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                          \"lua pipe child reset signal mask failed\");\n            exit(EXIT_FAILURE);\n        }\n\n        /* close listening socket fd */\n        ls = ngx_cycle->listening.elts;\n        for (i = 0; i < ngx_cycle->listening.nelts; i++) {\n            if (ls[i].fd != (ngx_socket_t) -1 &&\n                ngx_close_socket(ls[i].fd) == -1)\n            {\n                ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, ngx_socket_errno,\n                              \"lua pipe child \" ngx_close_socket_n\n                              \" %V failed\", &ls[i].addr_text);\n            }\n        }\n\n        /* close and dup pipefd */\n        if (close(in[1]) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                          \"lua pipe child failed to close the in[1] \"\n                          \"pipe fd\");\n        }\n\n        if (close(out[0]) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                          \"lua pipe child failed to close the out[0] \"\n                          \"pipe fd\");\n        }\n\n        if (ngx_cycle->log->file && ngx_cycle->log->file->fd == STDERR_FILENO) {\n            errlog_fd = ngx_cycle->log->file->fd;\n            temp_errlog_fd = dup(errlog_fd);\n\n            if (temp_errlog_fd == -1) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                              \"lua pipe child dup errlog fd failed\");\n                exit(EXIT_FAILURE);\n            }\n\n            if (ngx_cloexec(temp_errlog_fd) == -1) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                              \"lua pipe child new errlog fd \" ngx_cloexec_n\n                              \" failed\");\n            }\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                           \"lua pipe child dup old errlog fd %d to new fd %d\",\n                           ngx_cycle->log->file->fd, temp_errlog_fd);\n\n            ngx_cycle->log->file->fd = temp_errlog_fd;\n        }\n\n        if (dup2(in[0], STDIN_FILENO) == -1) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                          \"lua pipe child dup2 stdin failed\");\n            exit(EXIT_FAILURE);\n        }\n\n        if (dup2(out[1], STDOUT_FILENO) == -1) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                          \"lua pipe child dup2 stdout failed\");\n            exit(EXIT_FAILURE);\n        }\n\n        if (merge_stderr) {\n            if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                              \"lua pipe child dup2 stderr failed\");\n                exit(EXIT_FAILURE);\n            }\n\n        } else {\n            if (close(err[0]) == -1) {\n                ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                              \"lua pipe child failed to close the err[0] \"\n                              \"pipe fd\");\n            }\n\n            if (dup2(err[1], STDERR_FILENO) == -1) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                              \"lua pipe child dup2 stderr failed\");\n                exit(EXIT_FAILURE);\n            }\n        }\n\n        if (close(in[0]) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                          \"lua pipe failed to close the in[0]\");\n        }\n\n        if (close(out[1]) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                          \"lua pipe failed to close the out[1]\");\n        }\n\n        if (!merge_stderr) {\n            if (close(err[1]) == -1) {\n                ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                              \"lua pipe failed to close the err[1]\");\n            }\n        }\n\n        if (environ != NULL) {\n#if (NGX_HTTP_LUA_HAVE_EXECVPE)\n            if (execvpe(file, (char * const *) argv, (char * const *) environ)\n#else\n            if (ngx_http_lua_execvpe(file, (char * const *) argv,\n                                     (char * const *) environ)\n#endif\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                              \"lua pipe child execvpe() failed while \"\n                              \"executing %s\", file);\n            }\n\n        } else {\n            if (execvp(file, (char * const *) argv) == -1) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                              \"lua pipe child execvp() failed while \"\n                              \"executing %s\", file);\n            }\n        }\n\n        exit(EXIT_FAILURE);\n    }\n\n    /* parent process */\n    if (close(in[0]) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                      \"lua pipe: failed to close the in[0] pipe fd\");\n    }\n\n    stdin_fd = in[1];\n\n    if (ngx_nonblocking(stdin_fd) == -1) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    ngx_nonblocking_n \" failed: %s\",\n                                    strerror(errno))\n                       - errbuf;\n        goto close_in_out_err_fd;\n    }\n\n    pp->stdin_fd = stdin_fd;\n\n    if (close(out[1]) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                      \"lua pipe: failed to close the out[1] pipe fd\");\n    }\n\n    stdout_fd = out[0];\n\n    if (ngx_nonblocking(stdout_fd) == -1) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    ngx_nonblocking_n \" failed: %s\",\n                                    strerror(errno))\n                       - errbuf;\n        goto close_in_out_err_fd;\n    }\n\n    pp->stdout_fd = stdout_fd;\n\n    if (!merge_stderr) {\n        if (close(err[1]) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                          \"lua pipe: failed to close the err[1] pipe fd\");\n        }\n\n        stderr_fd = err[0];\n\n        if (ngx_nonblocking(stderr_fd) == -1) {\n            *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                        ngx_nonblocking_n \" failed: %s\",\n                                        strerror(errno))\n                           - errbuf;\n            goto close_in_out_err_fd;\n        }\n\n        pp->stderr_fd = stderr_fd;\n    }\n\n    if (pp->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n\n        if (cln == NULL) {\n            *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"no memory\")\n                           - errbuf;\n            goto close_in_out_err_fd;\n        }\n\n        cln->handler = (ngx_pool_cleanup_pt) ngx_http_lua_ffi_pipe_proc_destroy;\n        cln->data = proc;\n        pp->cleanup = &cln->handler;\n        pp->r = r;\n    }\n\n    node = (ngx_rbtree_node_t *) (pp + 1);\n    node->key = pid;\n    pipe_node = (ngx_http_lua_pipe_node_t *) &node->color;\n    pipe_node->proc = proc;\n    ngx_rbtree_insert(&ngx_http_lua_pipe_rbtree, node);\n\n    pp->node = node;\n    pp->pool = pool;\n    pp->merge_stderr = merge_stderr;\n    pp->buffer_size = buffer_size;\n\n    proc->_pid = pid;\n    proc->pipe = pp;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua pipe spawn process:%p pid:%P merge_stderr:%d \"\n                   \"buffer_size:%uz\", proc, pid, merge_stderr, buffer_size);\n    return NGX_OK;\n\nclose_in_out_err_fd:\n\n    if (!merge_stderr) {\n        if (close(err[0]) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                          \"failed to close the err[0] pipe fd\");\n        }\n\n        if (close(err[1]) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                          \"failed to close the err[1] pipe fd\");\n        }\n    }\n\nclose_in_out_fd:\n\n    if (close(out[0]) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                      \"failed to close the out[0] pipe fd\");\n    }\n\n    if (close(out[1]) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                      \"failed to close the out[1] pipe fd\");\n    }\n\nclose_in_fd:\n\n    if (close(in[0]) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                      \"failed to close the in[0] pipe fd\");\n    }\n\n    if (close(in[1]) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                      \"failed to close the in[1] pipe fd\");\n    }\n\nfree_pool:\n\n    ngx_destroy_pool(pool);\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_http_lua_pipe_close_helper(ngx_http_lua_pipe_t *pipe,\n    ngx_http_lua_pipe_ctx_t *pipe_ctx, ngx_event_t *ev)\n{\n    if (ev->handler != ngx_http_lua_pipe_dummy_event_handler) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua pipe abort blocking operation pipe_ctx:%p ev:%p\",\n                       pipe_ctx, ev);\n\n        if (pipe->dead) {\n            pipe_ctx->err_type = PIPE_ERR_CLOSED;\n\n        } else {\n            pipe_ctx->err_type = PIPE_ERR_ABORTED;\n        }\n\n        ngx_post_event(ev, &ngx_posted_events);\n        return;\n    }\n\n    ngx_close_connection(pipe_ctx->c);\n    pipe_ctx->c = NULL;\n}\n\n\nstatic void\nngx_http_lua_pipe_close_stdin(ngx_http_lua_pipe_t *pipe)\n{\n    ngx_event_t                     *wev;\n\n    if (pipe->stdin_ctx == NULL) {\n        if (pipe->stdin_fd != -1) {\n            if (close(pipe->stdin_fd) == -1) {\n                ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                              \"failed to close the stdin pipe fd\");\n            }\n\n            pipe->stdin_fd = -1;\n        }\n\n    } else if (pipe->stdin_ctx->c != NULL) {\n        wev = pipe->stdin_ctx->c->write;\n        ngx_http_lua_pipe_close_helper(pipe, pipe->stdin_ctx, wev);\n    }\n}\n\n\nstatic void\nngx_http_lua_pipe_close_stdout(ngx_http_lua_pipe_t *pipe)\n{\n    ngx_event_t                     *rev;\n\n    if (pipe->stdout_ctx == NULL) {\n        if (pipe->stdout_fd != -1) {\n            if (close(pipe->stdout_fd) == -1) {\n                ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                              \"failed to close the stdout pipe fd\");\n            }\n\n            pipe->stdout_fd = -1;\n        }\n\n    } else if (pipe->stdout_ctx->c != NULL) {\n        rev = pipe->stdout_ctx->c->read;\n        ngx_http_lua_pipe_close_helper(pipe, pipe->stdout_ctx, rev);\n    }\n}\n\n\nstatic void\nngx_http_lua_pipe_close_stderr(ngx_http_lua_pipe_t *pipe)\n{\n    ngx_event_t                     *rev;\n\n    if (pipe->stderr_ctx == NULL) {\n        if (pipe->stderr_fd != -1) {\n            if (close(pipe->stderr_fd) == -1) {\n                ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                              \"failed to close the stderr pipe fd\");\n            }\n\n            pipe->stderr_fd = -1;\n        }\n\n    } else if (pipe->stderr_ctx->c != NULL) {\n        rev = pipe->stderr_ctx->c->read;\n        ngx_http_lua_pipe_close_helper(pipe, pipe->stderr_ctx, rev);\n    }\n}\n\n\nint\nngx_http_lua_ffi_pipe_proc_shutdown_stdin(ngx_http_lua_ffi_pipe_proc_t *proc,\n    u_char *errbuf, size_t *errbuf_size)\n{\n    ngx_http_lua_pipe_t             *pipe;\n\n    pipe = proc->pipe;\n    if (pipe == NULL || pipe->closed) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"closed\") - errbuf;\n        return NGX_ERROR;\n    }\n\n    ngx_http_lua_pipe_close_stdin(pipe);\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_pipe_proc_shutdown_stdout(ngx_http_lua_ffi_pipe_proc_t *proc,\n    u_char *errbuf, size_t *errbuf_size)\n{\n    ngx_http_lua_pipe_t             *pipe;\n\n    pipe = proc->pipe;\n    if (pipe == NULL || pipe->closed) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"closed\") - errbuf;\n        return NGX_ERROR;\n    }\n\n    ngx_http_lua_pipe_close_stdout(pipe);\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_pipe_proc_shutdown_stderr(ngx_http_lua_ffi_pipe_proc_t *proc,\n    u_char *errbuf, size_t *errbuf_size)\n{\n    ngx_http_lua_pipe_t             *pipe;\n\n    pipe = proc->pipe;\n    if (pipe == NULL || pipe->closed) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"closed\") - errbuf;\n        return NGX_ERROR;\n    }\n\n    if (pipe->merge_stderr) {\n        /* stdout is used internally as stderr when merge_stderr is true */\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"merged to stdout\")\n                       - errbuf;\n        return NGX_ERROR;\n    }\n\n    ngx_http_lua_pipe_close_stderr(pipe);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_lua_pipe_proc_finalize(ngx_http_lua_ffi_pipe_proc_t *proc)\n{\n    ngx_http_lua_pipe_t          *pipe;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua pipe finalize process:%p pid:%P\",\n                   proc, proc->_pid);\n    pipe = proc->pipe;\n\n    if (pipe->node) {\n        ngx_rbtree_delete(&ngx_http_lua_pipe_rbtree, pipe->node);\n        pipe->node = NULL;\n    }\n\n    pipe->dead = 1;\n\n    ngx_http_lua_pipe_close_stdin(pipe);\n    ngx_http_lua_pipe_close_stdout(pipe);\n\n    if (!pipe->merge_stderr) {\n        ngx_http_lua_pipe_close_stderr(pipe);\n    }\n\n    pipe->closed = 1;\n}\n\n\nvoid\nngx_http_lua_ffi_pipe_proc_destroy(ngx_http_lua_ffi_pipe_proc_t *proc)\n{\n    ngx_http_lua_pipe_t          *pipe;\n\n    pipe = proc->pipe;\n    if (pipe == NULL) {\n        return;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua pipe destroy process:%p pid:%P\", proc, proc->_pid);\n\n    if (!pipe->dead) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua pipe kill process:%p pid:%P\", proc, proc->_pid);\n\n        if (kill(proc->_pid, SIGKILL) == -1) {\n            if (ngx_errno != ESRCH) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                              \"lua pipe failed to kill process:%p pid:%P\",\n                              proc, proc->_pid);\n            }\n        }\n    }\n\n    if (pipe->cleanup != NULL) {\n        *pipe->cleanup = NULL;\n        ngx_http_lua_cleanup_free(pipe->r, pipe->cleanup);\n        pipe->cleanup = NULL;\n    }\n\n    ngx_http_lua_pipe_proc_finalize(proc);\n\n    /*\n     * pipe_proc_finalize -> ngx_http_lua_pipe_close_helper may leave pipe\n     * connections open with active timers/posted events when there are\n     * pending I/O operations (handler != dummy_handler).  Close them now\n     * before destroying the pool.\n     *\n     * Without this, when pool cleanup LIFO ordering causes pipe_proc_destroy\n     * to run before request_cleanup_handler (e.g. QUIC connection close path:\n     * ngx_quic_close_streams -> ngx_http_free_request -> ngx_destroy_pool),\n     * request_cleanup sees proc->pipe == NULL and returns early, leaving the\n     * timer live.  The timer then fires after the request pool is freed,\n     * accessing a dangling wait_co_ctx pointer -> SIGSEGV.\n     *\n     * ngx_close_connection handles everything: timers (ngx_del_timer),\n     * posted events (ngx_delete_posted_event), epoll removal, fd close,\n     * and connection recycling.\n     */\n\n    if (pipe->stdout_ctx && pipe->stdout_ctx->c) {\n        ngx_close_connection(pipe->stdout_ctx->c);\n        pipe->stdout_ctx->c = NULL;\n    }\n\n    if (pipe->stderr_ctx && pipe->stderr_ctx->c) {\n        ngx_close_connection(pipe->stderr_ctx->c);\n        pipe->stderr_ctx->c = NULL;\n    }\n\n    if (pipe->stdin_ctx && pipe->stdin_ctx->c) {\n        ngx_close_connection(pipe->stdin_ctx->c);\n        pipe->stdin_ctx->c = NULL;\n    }\n\n    ngx_destroy_pool(pipe->pool);\n    proc->pipe = NULL;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_pipe_get_lua_ctx(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t **ctx, u_char *errbuf, size_t *errbuf_size)\n{\n    int                                 rc;\n\n    *ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (*ctx == NULL) {\n        return NGX_HTTP_LUA_FFI_NO_REQ_CTX;\n    }\n\n    rc = ngx_http_lua_ffi_check_context(*ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE,\n                                        errbuf, errbuf_size);\n    if (rc != NGX_OK) {\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_lua_pipe_put_error(ngx_http_lua_pipe_ctx_t *pipe_ctx, u_char *errbuf,\n    size_t *errbuf_size)\n{\n    switch (pipe_ctx->err_type) {\n\n    case PIPE_ERR_CLOSED:\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"closed\") - errbuf;\n        break;\n\n    case PIPE_ERR_SYSCALL:\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"%s\",\n                                    strerror(pipe_ctx->pipe_errno))\n                       - errbuf;\n        break;\n\n    case PIPE_ERR_NOMEM:\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"no memory\")\n                       - errbuf;\n        break;\n\n    case PIPE_ERR_TIMEOUT:\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"timeout\")\n                       - errbuf;\n        break;\n\n    case PIPE_ERR_ADD_READ_EV:\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"failed to add read event\")\n                       - errbuf;\n        break;\n\n    case PIPE_ERR_ADD_WRITE_EV:\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"failed to add write event\")\n                       - errbuf;\n        break;\n\n    case PIPE_ERR_ABORTED:\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"aborted\") - errbuf;\n        break;\n\n    default:\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"unexpected err type: %d\", pipe_ctx->err_type);\n        ngx_http_lua_assert(NULL);\n    }\n}\n\n\nstatic void\nngx_http_lua_pipe_put_data(ngx_http_lua_pipe_t *pipe,\n    ngx_http_lua_pipe_ctx_t *pipe_ctx, u_char **buf, size_t *buf_size)\n{\n    size_t                   size = 0;\n    size_t                   chunk_size;\n    size_t                   nbufs;\n    u_char                  *p;\n    ngx_buf_t               *b;\n    ngx_chain_t             *cl;\n    ngx_chain_t            **ll;\n\n    nbufs = 0;\n    ll = NULL;\n\n    for (cl = pipe_ctx->bufs_in; cl; cl = cl->next) {\n        b = cl->buf;\n        chunk_size = b->last - b->pos;\n\n        if (cl->next) {\n            ll = &cl->next;\n        }\n\n        size += chunk_size;\n\n        nbufs++;\n    }\n\n    if (*buf_size < size) {\n        *buf = NULL;\n        *buf_size = size;\n\n        return;\n    }\n\n    *buf_size = size;\n\n    p = *buf;\n    for (cl = pipe_ctx->bufs_in; cl; cl = cl->next) {\n        b = cl->buf;\n        chunk_size = b->last - b->pos;\n        p = ngx_cpymem(p, b->pos, chunk_size);\n    }\n\n    if (nbufs > 1 && ll) {\n        *ll = pipe->free_bufs;\n        pipe->free_bufs = pipe_ctx->bufs_in;\n        pipe_ctx->bufs_in = pipe_ctx->buf_in;\n    }\n\n    if (pipe_ctx->buffer.pos == pipe_ctx->buffer.last) {\n        pipe_ctx->buffer.pos = pipe_ctx->buffer.start;\n        pipe_ctx->buffer.last = pipe_ctx->buffer.start;\n    }\n\n    if (pipe_ctx->bufs_in) {\n        pipe_ctx->buf_in->buf->last = pipe_ctx->buffer.pos;\n        pipe_ctx->buf_in->buf->pos = pipe_ctx->buffer.pos;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_lua_pipe_add_input_buffer(ngx_http_lua_pipe_t *pipe,\n    ngx_http_lua_pipe_ctx_t *pipe_ctx)\n{\n    ngx_chain_t             *cl;\n\n    cl = ngx_http_lua_chain_get_free_buf(ngx_cycle->log, pipe->pool,\n                                         &pipe->free_bufs,\n                                         pipe->buffer_size);\n\n    if (cl == NULL) {\n        pipe_ctx->err_type = PIPE_ERR_NOMEM;\n        return NGX_ERROR;\n    }\n\n    pipe_ctx->buf_in->next = cl;\n    pipe_ctx->buf_in = cl;\n    pipe_ctx->buffer = *cl->buf;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_pipe_read_all(void *data, ssize_t bytes)\n{\n    ngx_http_lua_pipe_ctx_t      *pipe_ctx = data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, \"lua pipe read all\");\n    return ngx_http_lua_read_all(&pipe_ctx->buffer, pipe_ctx->buf_in, bytes,\n                                 ngx_cycle->log);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_pipe_read_bytes(void *data, ssize_t bytes)\n{\n    ngx_int_t                          rc;\n    ngx_http_lua_pipe_ctx_t           *pipe_ctx = data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua pipe read bytes %z\", bytes);\n\n    rc = ngx_http_lua_read_bytes(&pipe_ctx->buffer, pipe_ctx->buf_in,\n                                 &pipe_ctx->rest, bytes, ngx_cycle->log);\n    if (rc == NGX_ERROR) {\n        pipe_ctx->err_type = PIPE_ERR_CLOSED;\n        return NGX_ERROR;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_pipe_read_line(void *data, ssize_t bytes)\n{\n    ngx_int_t                          rc;\n    ngx_http_lua_pipe_ctx_t           *pipe_ctx = data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua pipe read line\");\n    rc = ngx_http_lua_read_line(&pipe_ctx->buffer, pipe_ctx->buf_in, bytes,\n                                ngx_cycle->log);\n    if (rc == NGX_ERROR) {\n        pipe_ctx->err_type = PIPE_ERR_CLOSED;\n        return NGX_ERROR;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_pipe_read_any(void *data, ssize_t bytes)\n{\n    ngx_int_t                          rc;\n    ngx_http_lua_pipe_ctx_t           *pipe_ctx = data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, \"lua pipe read any\");\n    rc = ngx_http_lua_read_any(&pipe_ctx->buffer, pipe_ctx->buf_in,\n                               &pipe_ctx->rest, bytes, ngx_cycle->log);\n    if (rc == NGX_ERROR) {\n        pipe_ctx->err_type = PIPE_ERR_CLOSED;\n        return NGX_ERROR;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_pipe_read(ngx_http_lua_pipe_t *pipe,\n    ngx_http_lua_pipe_ctx_t *pipe_ctx)\n{\n    int                                 rc;\n    int                                 read;\n    size_t                              size;\n    ssize_t                             n;\n    ngx_buf_t                          *b;\n    ngx_event_t                        *rev;\n    ngx_connection_t                   *c;\n\n    c = pipe_ctx->c;\n    rev = c->read;\n    b = &pipe_ctx->buffer;\n    read = 0;\n\n    for ( ;; ) {\n        size = b->last - b->pos;\n\n        if (size || pipe_ctx->eof) {\n            rc = pipe_ctx->input_filter(pipe_ctx->input_filter_ctx, size);\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (rc == NGX_OK) {\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                               \"lua pipe read done pipe:%p\", pipe_ctx);\n                return NGX_OK;\n            }\n\n            /* rc == NGX_AGAIN */\n            continue;\n        }\n\n        if (read && !rev->ready) {\n            break;\n        }\n\n        size = b->end - b->last;\n\n        if (size == 0) {\n            rc = ngx_http_lua_pipe_add_input_buffer(pipe, pipe_ctx);\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            b = &pipe_ctx->buffer;\n            size = (size_t) (b->end - b->last);\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua pipe try to read data %uz pipe:%p\",\n                       size, pipe_ctx);\n\n        n = c->recv(c, b->last, size);\n        read = 1;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua pipe read data returned %z pipe:%p\", n, pipe_ctx);\n\n        if (n == NGX_AGAIN) {\n            break;\n        }\n\n        if (n == 0) {\n            pipe_ctx->eof = 1;\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                           \"lua pipe closed pipe:%p\", pipe_ctx);\n            continue;\n        }\n\n        if (n == NGX_ERROR) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, ngx_errno,\n                           \"lua pipe read data error pipe:%p\", pipe_ctx);\n\n            pipe_ctx->err_type = PIPE_ERR_SYSCALL;\n            pipe_ctx->pipe_errno = ngx_errno;\n            return NGX_ERROR;\n        }\n\n        b->last += n;\n    }\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_pipe_init_ctx(ngx_http_lua_pipe_ctx_t **pipe_ctx_pt, int fd,\n    ngx_pool_t *pool, u_char *errbuf, size_t *errbuf_size)\n{\n    ngx_connection_t                   *c;\n\n    if (fd == -1) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"closed\") - errbuf;\n        return NGX_ERROR;\n    }\n\n    *pipe_ctx_pt = ngx_pcalloc(pool, sizeof(ngx_http_lua_pipe_ctx_t));\n    if (*pipe_ctx_pt == NULL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"no memory\")\n                       - errbuf;\n        return NGX_ERROR;\n    }\n\n    c = ngx_get_connection(fd, ngx_cycle->log);\n    if (c == NULL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"no connection\")\n                       - errbuf;\n        return NGX_ERROR;\n    }\n\n    c->log = ngx_cycle->log;\n    c->recv = ngx_http_lua_pipe_fd_read;\n    c->read->handler = ngx_http_lua_pipe_dummy_event_handler;\n    c->read->log = c->log;\n\n#ifdef HAVE_SOCKET_CLOEXEC_PATCH\n    c->read->skip_socket_leak_check = 1;\n#endif\n\n    c->send = ngx_http_lua_pipe_fd_write;\n    c->write->handler = ngx_http_lua_pipe_dummy_event_handler;\n    c->write->log = c->log;\n    (*pipe_ctx_pt)->c = c;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua pipe init pipe ctx:%p fd:*%d\", *pipe_ctx_pt, fd);\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_pipe_proc_read(ngx_http_request_t *r,\n    ngx_http_lua_ffi_pipe_proc_t *proc, int from_stderr, int reader_type,\n    size_t length, u_char **buf, size_t *buf_size, u_char *errbuf,\n    size_t *errbuf_size)\n{\n    int                                 rc;\n    ngx_msec_t                          timeout;\n    ngx_event_t                        *rev;\n    ngx_connection_t                   *c;\n    ngx_http_lua_ctx_t                 *ctx;\n    ngx_http_lua_pipe_t                *pipe;\n    ngx_http_lua_co_ctx_t              *wait_co_ctx;\n    ngx_http_lua_pipe_ctx_t            *pipe_ctx;\n\n    rc = ngx_http_lua_pipe_get_lua_ctx(r, &ctx, errbuf, errbuf_size);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua pipe read process:%p pid:%P\", proc, proc->_pid);\n\n    pipe = proc->pipe;\n    if (pipe == NULL || pipe->closed) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"closed\") - errbuf;\n        return NGX_ERROR;\n    }\n\n    if (pipe->merge_stderr && from_stderr) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"merged to stdout\")\n                       - errbuf;\n        return NGX_ERROR;\n    }\n\n    if (from_stderr) {\n        if (pipe->stderr_ctx == NULL) {\n            if (ngx_http_lua_pipe_init_ctx(&pipe->stderr_ctx, pipe->stderr_fd,\n                                           pipe->pool, errbuf,\n                                           errbuf_size)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n        } else {\n            pipe->stderr_ctx->err_type = 0;\n        }\n\n        pipe_ctx = pipe->stderr_ctx;\n\n    } else {\n        if (pipe->stdout_ctx == NULL) {\n            if (ngx_http_lua_pipe_init_ctx(&pipe->stdout_ctx, pipe->stdout_fd,\n                                           pipe->pool, errbuf,\n                                           errbuf_size)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n        } else {\n            pipe->stdout_ctx->err_type = 0;\n        }\n\n        pipe_ctx = pipe->stdout_ctx;\n    }\n\n    c = pipe_ctx->c;\n    if (c == NULL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"closed\") - errbuf;\n        return NGX_ERROR;\n    }\n\n    rev = c->read;\n    if (rev->handler != ngx_http_lua_pipe_dummy_event_handler) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"pipe busy reading\")\n                       - errbuf;\n        return NGX_ERROR;\n    }\n\n    pipe_ctx->input_filter_ctx = pipe_ctx;\n\n    switch (reader_type) {\n\n    case PIPE_READ_ALL:\n        pipe_ctx->input_filter = ngx_http_lua_pipe_read_all;\n        break;\n\n    case PIPE_READ_BYTES:\n        pipe_ctx->input_filter = ngx_http_lua_pipe_read_bytes;\n        break;\n\n    case PIPE_READ_LINE:\n        pipe_ctx->input_filter = ngx_http_lua_pipe_read_line;\n        break;\n\n    case PIPE_READ_ANY:\n        pipe_ctx->input_filter = ngx_http_lua_pipe_read_any;\n        break;\n\n    default:\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"unexpected reader_type: %d\", reader_type);\n        ngx_http_lua_assert(NULL);\n    }\n\n    pipe_ctx->rest = length;\n\n    if (pipe_ctx->bufs_in == NULL) {\n        pipe_ctx->bufs_in =\n            ngx_http_lua_chain_get_free_buf(ngx_cycle->log, pipe->pool,\n                                            &pipe->free_bufs,\n                                            pipe->buffer_size);\n\n        if (pipe_ctx->bufs_in == NULL) {\n            pipe_ctx->err_type = PIPE_ERR_NOMEM;\n            goto error;\n        }\n\n        pipe_ctx->buf_in = pipe_ctx->bufs_in;\n        pipe_ctx->buffer = *pipe_ctx->buf_in->buf;\n    }\n\n    rc = ngx_http_lua_pipe_read(pipe, pipe_ctx);\n    if (rc == NGX_ERROR) {\n        goto error;\n    }\n\n    if (rc == NGX_OK) {\n        ngx_http_lua_pipe_put_data(pipe, pipe_ctx, buf, buf_size);\n        return NGX_OK;\n    }\n\n    /* rc == NGX_AGAIN */\n    wait_co_ctx = ctx->cur_co_ctx;\n\n    c->data = wait_co_ctx;\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        pipe_ctx->err_type = PIPE_ERR_ADD_READ_EV;\n        goto error;\n    }\n\n    wait_co_ctx->data = proc;\n\n    if (from_stderr) {\n        rev->handler = ngx_http_lua_pipe_resume_read_stderr_handler;\n        wait_co_ctx->cleanup = ngx_http_lua_pipe_proc_read_stderr_cleanup;\n        timeout = proc->stderr_read_timeout;\n\n    } else {\n        rev->handler = ngx_http_lua_pipe_resume_read_stdout_handler;\n        wait_co_ctx->cleanup = ngx_http_lua_pipe_proc_read_stdout_cleanup;\n        timeout = proc->stdout_read_timeout;\n    }\n\n    if (timeout > 0) {\n        ngx_add_timer(rev, timeout);\n        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua pipe add timer for reading: %d(ms) process:%p \"\n                       \"pid:%P pipe:%p ev:%p\", timeout, proc, proc->_pid, pipe,\n                       rev);\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua pipe read yielding process:%p pid:%P pipe:%p\", proc,\n                   proc->_pid, pipe);\n\n    return NGX_AGAIN;\n\nerror:\n\n    if (pipe_ctx->bufs_in) {\n        ngx_http_lua_pipe_put_data(pipe, pipe_ctx, buf, buf_size);\n        ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size);\n        return NGX_DECLINED;\n    }\n\n    ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size);\n\n    return NGX_ERROR;\n}\n\n\n/*\n * ngx_http_lua_ffi_pipe_get_read_result should only be called just after\n * ngx_http_lua_ffi_pipe_proc_read, so we omit most of the sanity check already\n * done in ngx_http_lua_ffi_pipe_proc_read.\n */\nint\nngx_http_lua_ffi_pipe_get_read_result(ngx_http_request_t *r,\n    ngx_http_lua_ffi_pipe_proc_t *proc, int from_stderr, u_char **buf,\n    size_t *buf_size, u_char *errbuf, size_t *errbuf_size)\n{\n    ngx_http_lua_pipe_t                *pipe;\n    ngx_http_lua_pipe_ctx_t            *pipe_ctx;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua pipe get read result process:%p pid:%P\", proc,\n                   proc->_pid);\n\n    pipe = proc->pipe;\n    pipe_ctx = from_stderr ? pipe->stderr_ctx : pipe->stdout_ctx;\n\n    if (!pipe_ctx->err_type) {\n        ngx_http_lua_pipe_put_data(pipe, pipe_ctx, buf, buf_size);\n        return NGX_OK;\n    }\n\n    if (pipe_ctx->bufs_in) {\n        ngx_http_lua_pipe_put_data(pipe, pipe_ctx, buf, buf_size);\n        ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size);\n        return NGX_DECLINED;\n    }\n\n    ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size);\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_pipe_write(ngx_http_lua_pipe_t *pipe,\n    ngx_http_lua_pipe_ctx_t *pipe_ctx)\n{\n    size_t                       size;\n    ngx_int_t                    n;\n    ngx_buf_t                   *b;\n    ngx_connection_t            *c;\n\n    c = pipe_ctx->c;\n    b = pipe_ctx->buf_in->buf;\n\n    for ( ;; ) {\n        size = b->last - b->pos;\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua pipe try to write data %uz pipe:%p\", size,\n                       pipe_ctx);\n\n        n = c->send(c, b->pos, size);\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua pipe write returned %i pipe:%p\", n, pipe_ctx);\n\n        if (n >= 0) {\n            b->pos += n;\n\n            if (b->pos == b->last) {\n                b->pos = b->start;\n                b->last = b->start;\n\n                if (!pipe->free_bufs) {\n                    pipe->free_bufs = pipe_ctx->buf_in;\n\n                } else {\n                    pipe->free_bufs->next = pipe_ctx->buf_in;\n                }\n\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                               \"lua pipe write done pipe:%p\", pipe_ctx);\n                return NGX_OK;\n            }\n\n            continue;\n        }\n\n        /* NGX_ERROR || NGX_AGAIN */\n        break;\n    }\n\n    if (n == NGX_ERROR) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, ngx_errno,\n                       \"lua pipe write data error pipe:%p\", pipe_ctx);\n\n        if (ngx_errno == NGX_EPIPE) {\n            pipe_ctx->err_type = PIPE_ERR_CLOSED;\n\n        } else {\n            pipe_ctx->err_type = PIPE_ERR_SYSCALL;\n            pipe_ctx->pipe_errno = ngx_errno;\n        }\n\n        return NGX_ERROR;\n    }\n\n    return NGX_AGAIN;\n}\n\n\nssize_t\nngx_http_lua_ffi_pipe_proc_write(ngx_http_request_t *r,\n    ngx_http_lua_ffi_pipe_proc_t *proc, const u_char *data, size_t len,\n    u_char *errbuf, size_t *errbuf_size)\n{\n    int                                 rc;\n    ngx_buf_t                          *b;\n    ngx_msec_t                          timeout;\n    ngx_chain_t                        *cl;\n    ngx_event_t                        *wev;\n    ngx_http_lua_ctx_t                 *ctx;\n    ngx_http_lua_pipe_t                *pipe;\n    ngx_http_lua_co_ctx_t              *wait_co_ctx;\n    ngx_http_lua_pipe_ctx_t            *pipe_ctx;\n\n    rc = ngx_http_lua_pipe_get_lua_ctx(r, &ctx, errbuf, errbuf_size);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua pipe write process:%p pid:%P\", proc, proc->_pid);\n\n    pipe = proc->pipe;\n    if (pipe == NULL || pipe->closed) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"closed\") - errbuf;\n        return NGX_ERROR;\n    }\n\n    if (pipe->stdin_ctx == NULL) {\n        if (ngx_http_lua_pipe_init_ctx(&pipe->stdin_ctx, pipe->stdin_fd,\n                                       pipe->pool, errbuf,\n                                       errbuf_size)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n    } else {\n        pipe->stdin_ctx->err_type = 0;\n    }\n\n    pipe_ctx = pipe->stdin_ctx;\n    if (pipe_ctx->c == NULL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"closed\") - errbuf;\n        return NGX_ERROR;\n    }\n\n    wev = pipe_ctx->c->write;\n    if (wev->handler != ngx_http_lua_pipe_dummy_event_handler) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"pipe busy writing\")\n                       - errbuf;\n        return NGX_ERROR;\n    }\n\n    pipe_ctx->rest = len;\n\n    cl = ngx_http_lua_chain_get_free_buf(ngx_cycle->log, pipe->pool,\n                                         &pipe->free_bufs, len);\n    if (cl == NULL) {\n        pipe_ctx->err_type = PIPE_ERR_NOMEM;\n        goto error;\n    }\n\n    pipe_ctx->buf_in = cl;\n    b = pipe_ctx->buf_in->buf;\n    b->last = ngx_copy(b->last, data, len);\n\n    rc = ngx_http_lua_pipe_write(pipe, pipe_ctx);\n    if (rc == NGX_ERROR) {\n        goto error;\n    }\n\n    if (rc == NGX_OK) {\n        return len;\n    }\n\n    /* rc == NGX_AGAIN */\n    wait_co_ctx = ctx->cur_co_ctx;\n    pipe_ctx->c->data = wait_co_ctx;\n\n    wev->handler = ngx_http_lua_pipe_resume_write_handler;\n    if (ngx_handle_write_event(wev, 0) != NGX_OK) {\n        pipe_ctx->err_type = PIPE_ERR_ADD_WRITE_EV;\n        goto error;\n    }\n\n    wait_co_ctx->data = proc;\n    wait_co_ctx->cleanup = ngx_http_lua_pipe_proc_write_cleanup;\n    timeout = proc->write_timeout;\n\n    if (timeout > 0) {\n        ngx_add_timer(wev, timeout);\n        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua pipe add timer for writing: %d(ms) process:%p \"\n                       \"pid:%P pipe:%p ev:%p\", timeout, proc, proc->_pid, pipe,\n                       wev);\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua pipe write yielding process:%p pid:%P pipe:%p\", proc,\n                   proc->_pid, pipe);\n\n    return NGX_AGAIN;\n\nerror:\n\n    ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size);\n    return NGX_ERROR;\n}\n\n\n/*\n * ngx_http_lua_ffi_pipe_get_write_result should only be called just after\n * ngx_http_lua_ffi_pipe_proc_write, so we omit most of the sanity check\n * already done in ngx_http_lua_ffi_pipe_proc_write.\n */\nssize_t\nngx_http_lua_ffi_pipe_get_write_result(ngx_http_request_t *r,\n    ngx_http_lua_ffi_pipe_proc_t *proc, u_char *errbuf, size_t *errbuf_size)\n{\n    ngx_http_lua_pipe_t                *pipe;\n    ngx_http_lua_pipe_ctx_t            *pipe_ctx;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua pipe get write result process:%p pid:%P\", proc,\n                   proc->_pid);\n\n    pipe = proc->pipe;\n    pipe_ctx = pipe->stdin_ctx;\n\n    if (pipe_ctx->err_type) {\n        ngx_http_lua_pipe_put_error(pipe_ctx, errbuf, errbuf_size);\n        return NGX_ERROR;\n    }\n\n    return pipe_ctx->rest;\n}\n\n\nint\nngx_http_lua_ffi_pipe_proc_wait(ngx_http_request_t *r,\n    ngx_http_lua_ffi_pipe_proc_t *proc, char **reason, int *status,\n    u_char *errbuf, size_t *errbuf_size)\n{\n    int                                 rc;\n    ngx_rbtree_node_t                  *node;\n    ngx_http_lua_ctx_t                 *ctx;\n    ngx_http_lua_pipe_t                *pipe;\n    ngx_http_lua_co_ctx_t              *wait_co_ctx;\n    ngx_http_lua_pipe_node_t           *pipe_node;\n\n    rc = ngx_http_lua_pipe_get_lua_ctx(r, &ctx, errbuf, errbuf_size);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua pipe wait process:%p pid:%P\", proc, proc->_pid);\n\n    pipe = proc->pipe;\n    if (pipe == NULL || pipe->closed) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"exited\") - errbuf;\n        return NGX_ERROR;\n    }\n\n    node = pipe->node;\n    pipe_node = (ngx_http_lua_pipe_node_t *) &node->color;\n    if (pipe_node->wait_co_ctx) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"pipe busy waiting\")\n                       - errbuf;\n        return NGX_ERROR;\n    }\n\n    if (pipe_node->reason_code == REASON_RUNNING_CODE) {\n        wait_co_ctx = ctx->cur_co_ctx;\n        wait_co_ctx->data = proc;\n        ngx_memzero(&wait_co_ctx->sleep, sizeof(ngx_event_t));\n        wait_co_ctx->sleep.handler = ngx_http_lua_pipe_resume_wait_handler;\n        wait_co_ctx->sleep.data = wait_co_ctx;\n        wait_co_ctx->sleep.log = r->connection->log;\n        wait_co_ctx->cleanup = ngx_http_lua_pipe_proc_wait_cleanup;\n\n        pipe_node->wait_co_ctx = wait_co_ctx;\n\n        if (proc->wait_timeout > 0) {\n            ngx_add_timer(&wait_co_ctx->sleep, proc->wait_timeout);\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"lua pipe add timer for waiting: %d(ms) process:%p \"\n                           \"pid:%P ev:%p\", proc->wait_timeout, proc,\n                           proc->_pid, &wait_co_ctx->sleep);\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua pipe wait yielding process:%p pid:%P\", proc,\n                       proc->_pid);\n\n        return NGX_AGAIN;\n    }\n\n    *status = pipe_node->status;\n\n    switch (pipe_node->reason_code) {\n\n    case REASON_EXIT_CODE:\n        *reason = REASON_EXIT;\n        break;\n\n    case REASON_SIGNAL_CODE:\n        *reason = REASON_SIGNAL;\n        break;\n\n    default:\n        *reason = REASON_UNKNOWN;\n    }\n\n    ngx_http_lua_pipe_proc_finalize(proc);\n\n    if (*status == 0) {\n        return NGX_OK;\n    }\n\n    return NGX_DECLINED;\n}\n\n\nint\nngx_http_lua_ffi_pipe_proc_kill(ngx_http_lua_ffi_pipe_proc_t *proc, int signal,\n    u_char *errbuf, size_t *errbuf_size)\n{\n    ngx_pid_t                           pid;\n    ngx_http_lua_pipe_t                *pipe;\n\n    pipe = proc->pipe;\n\n    if (pipe == NULL || pipe->dead) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"exited\") - errbuf;\n        return NGX_ERROR;\n    }\n\n    pid = proc->_pid;\n\n    if (kill(pid, signal) == -1) {\n        switch (ngx_errno) {\n        case EINVAL:\n            *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"invalid signal\")\n                           - errbuf;\n            break;\n\n        case ESRCH:\n            *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"exited\")\n                           - errbuf;\n            break;\n\n        default:\n            *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, \"%s\",\n                                        strerror(ngx_errno))\n                           - errbuf;\n        }\n\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic int\nngx_http_lua_pipe_read_stdout_retval(ngx_http_lua_ffi_pipe_proc_t *proc,\n    lua_State *L)\n{\n    return ngx_http_lua_pipe_read_retval_helper(proc, L, 0);\n}\n\n\nstatic int\nngx_http_lua_pipe_read_stderr_retval(ngx_http_lua_ffi_pipe_proc_t *proc,\n    lua_State *L)\n{\n    return ngx_http_lua_pipe_read_retval_helper(proc, L, 1);\n}\n\n\nstatic int\nngx_http_lua_pipe_read_retval_helper(ngx_http_lua_ffi_pipe_proc_t *proc,\n    lua_State *L, int from_stderr)\n{\n    int                              rc;\n    ngx_msec_t                       timeout;\n    ngx_event_t                     *rev;\n    ngx_http_lua_pipe_t             *pipe;\n    ngx_http_lua_pipe_ctx_t         *pipe_ctx;\n\n    pipe = proc->pipe;\n    if (from_stderr) {\n        pipe_ctx = pipe->stderr_ctx;\n\n    } else {\n        pipe_ctx = pipe->stdout_ctx;\n    }\n\n    if (pipe->timeout) {\n        pipe->timeout = 0;\n        pipe_ctx->err_type = PIPE_ERR_TIMEOUT;\n        return 0;\n    }\n\n    if (pipe_ctx->err_type == PIPE_ERR_ABORTED) {\n        ngx_close_connection(pipe_ctx->c);\n        pipe_ctx->c = NULL;\n        return 0;\n    }\n\n    rc = ngx_http_lua_pipe_read(pipe, pipe_ctx);\n    if (rc != NGX_AGAIN) {\n        return 0;\n    }\n\n    rev = pipe_ctx->c->read;\n\n    if (from_stderr) {\n        rev->handler = ngx_http_lua_pipe_resume_read_stderr_handler;\n        timeout = proc->stderr_read_timeout;\n\n    } else {\n        rev->handler = ngx_http_lua_pipe_resume_read_stdout_handler;\n        timeout = proc->stdout_read_timeout;\n    }\n\n    if (timeout > 0) {\n        ngx_add_timer(rev, timeout);\n        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua pipe add timer for reading: %d(ms) proc:%p \"\n                       \"pid:%P pipe:%p ev:%p\", timeout, proc, proc->_pid, pipe,\n                       rev);\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua pipe read yielding process:%p pid:%P pipe:%p\", proc,\n                   proc->_pid, pipe);\n\n    return NGX_AGAIN;\n}\n\n\nstatic int\nngx_http_lua_pipe_write_retval(ngx_http_lua_ffi_pipe_proc_t *proc,\n    lua_State *L)\n{\n    int                              rc;\n    ngx_msec_t                       timeout;\n    ngx_event_t                     *wev;\n    ngx_http_lua_pipe_t             *pipe;\n    ngx_http_lua_pipe_ctx_t         *pipe_ctx;\n\n    pipe = proc->pipe;\n    pipe_ctx = pipe->stdin_ctx;\n\n    if (pipe->timeout) {\n        pipe->timeout = 0;\n        pipe_ctx->err_type = PIPE_ERR_TIMEOUT;\n        return 0;\n    }\n\n    if (pipe_ctx->err_type == PIPE_ERR_ABORTED) {\n        ngx_close_connection(pipe_ctx->c);\n        pipe_ctx->c = NULL;\n        return 0;\n    }\n\n    rc = ngx_http_lua_pipe_write(pipe, pipe_ctx);\n    if (rc != NGX_AGAIN) {\n        return 0;\n    }\n\n    wev = pipe_ctx->c->write;\n    wev->handler = ngx_http_lua_pipe_resume_write_handler;\n    timeout = proc->write_timeout;\n\n    if (timeout > 0) {\n        ngx_add_timer(wev, timeout);\n        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua pipe add timer for writing: %d(ms) proc:%p \"\n                       \"pid:%P pipe:%p ev:%p\", timeout, proc, proc->_pid, pipe,\n                       wev);\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua pipe write yielding process:%p pid:%P pipe:%p\", proc,\n                   proc->_pid, pipe);\n\n    return NGX_AGAIN;\n}\n\n\nstatic int\nngx_http_lua_pipe_wait_retval(ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L)\n{\n    int                              nret;\n    ngx_rbtree_node_t               *node;\n    ngx_http_lua_pipe_t             *pipe;\n    ngx_http_lua_pipe_node_t        *pipe_node;\n\n    pipe = proc->pipe;\n    node = pipe->node;\n    pipe_node = (ngx_http_lua_pipe_node_t *) &node->color;\n    pipe_node->wait_co_ctx = NULL;\n\n    if (pipe->timeout) {\n        pipe->timeout = 0;\n        lua_pushnil(L);\n        lua_pushliteral(L, \"timeout\");\n        return 2;\n    }\n\n    ngx_http_lua_pipe_proc_finalize(pipe_node->proc);\n\n    if (pipe_node->status == 0) {\n        lua_pushboolean(L, 1);\n        lua_pushliteral(L, REASON_EXIT);\n        lua_pushinteger(L, pipe_node->status);\n        nret = 3;\n\n    } else {\n        lua_pushboolean(L, 0);\n\n        switch (pipe_node->reason_code) {\n\n        case REASON_EXIT_CODE:\n            lua_pushliteral(L, REASON_EXIT);\n            break;\n\n        case REASON_SIGNAL_CODE:\n            lua_pushliteral(L, REASON_SIGNAL);\n            break;\n\n        default:\n            lua_pushliteral(L, REASON_UNKNOWN);\n        }\n\n        lua_pushinteger(L, pipe_node->status);\n        nret = 3;\n    }\n\n    return nret;\n}\n\n\nstatic void\nngx_http_lua_pipe_resume_helper(ngx_event_t *ev,\n    ngx_http_lua_co_ctx_t *wait_co_ctx)\n{\n    ngx_connection_t                *c;\n    ngx_http_request_t              *r;\n    ngx_http_lua_ctx_t              *ctx;\n    ngx_http_lua_pipe_t             *pipe;\n    ngx_http_lua_ffi_pipe_proc_t    *proc;\n\n    if (ev->timedout) {\n        proc = wait_co_ctx->data;\n        pipe = proc->pipe;\n        pipe->timeout = 1;\n        ev->timedout = 0;\n    }\n\n    ngx_http_lua_pipe_clear_event(ev);\n\n    r = ngx_http_lua_get_req(wait_co_ctx->co);\n    c = r->connection;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    ngx_http_lua_assert(ctx != NULL);\n\n    ctx->cur_co_ctx = wait_co_ctx;\n\n    if (ctx->entered_content_phase) {\n        (void) ngx_http_lua_pipe_resume(r);\n\n    } else {\n        ctx->resume_handler = ngx_http_lua_pipe_resume;\n        ngx_http_core_run_phases(r);\n    }\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nstatic void\nngx_http_lua_pipe_resume_read_stdout_handler(ngx_event_t *ev)\n{\n    ngx_connection_t                *c = ev->data;\n    ngx_http_lua_co_ctx_t           *wait_co_ctx;\n    ngx_http_lua_pipe_t             *pipe;\n    ngx_http_lua_ffi_pipe_proc_t    *proc;\n\n    wait_co_ctx = c->data;\n    proc = wait_co_ctx->data;\n    pipe = proc->pipe;\n    pipe->retval_handler = ngx_http_lua_pipe_read_stdout_retval;\n    ngx_http_lua_pipe_resume_helper(ev, wait_co_ctx);\n}\n\n\nstatic void\nngx_http_lua_pipe_resume_read_stderr_handler(ngx_event_t *ev)\n{\n    ngx_connection_t                *c = ev->data;\n    ngx_http_lua_co_ctx_t           *wait_co_ctx;\n    ngx_http_lua_pipe_t             *pipe;\n    ngx_http_lua_ffi_pipe_proc_t    *proc;\n\n    wait_co_ctx = c->data;\n    proc = wait_co_ctx->data;\n    pipe = proc->pipe;\n    pipe->retval_handler = ngx_http_lua_pipe_read_stderr_retval;\n    ngx_http_lua_pipe_resume_helper(ev, wait_co_ctx);\n}\n\n\nstatic void\nngx_http_lua_pipe_resume_write_handler(ngx_event_t *ev)\n{\n    ngx_connection_t                *c = ev->data;\n    ngx_http_lua_co_ctx_t           *wait_co_ctx;\n    ngx_http_lua_pipe_t             *pipe;\n    ngx_http_lua_ffi_pipe_proc_t    *proc;\n\n    wait_co_ctx = c->data;\n    proc = wait_co_ctx->data;\n    pipe = proc->pipe;\n    pipe->retval_handler = ngx_http_lua_pipe_write_retval;\n    ngx_http_lua_pipe_resume_helper(ev, wait_co_ctx);\n}\n\n\nstatic void\nngx_http_lua_pipe_resume_wait_handler(ngx_event_t *ev)\n{\n    ngx_http_lua_co_ctx_t           *wait_co_ctx = ev->data;\n    ngx_http_lua_pipe_t             *pipe;\n    ngx_http_lua_ffi_pipe_proc_t    *proc;\n\n    proc = wait_co_ctx->data;\n    pipe = proc->pipe;\n    pipe->retval_handler = ngx_http_lua_pipe_wait_retval;\n    ngx_http_lua_pipe_resume_helper(ev, wait_co_ctx);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_pipe_resume(ngx_http_request_t *r)\n{\n    int                              nret;\n    lua_State                       *vm;\n    ngx_int_t                        rc;\n    ngx_uint_t                       nreqs;\n    ngx_connection_t                *c;\n    ngx_http_lua_ctx_t              *ctx;\n    ngx_http_lua_pipe_t             *pipe;\n    ngx_http_lua_ffi_pipe_proc_t    *proc;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->resume_handler = ngx_http_lua_wev_handler;\n    ctx->cur_co_ctx->cleanup = NULL;\n\n    proc = ctx->cur_co_ctx->data;\n    pipe = proc->pipe;\n    nret = pipe->retval_handler(proc, ctx->cur_co_ctx->co);\n    if (nret == NGX_AGAIN) {\n        return NGX_DONE;\n    }\n\n    c = r->connection;\n    vm = ngx_http_lua_get_lua_vm(r, ctx);\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(vm, r, ctx, nret);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run thread returned %d\", rc);\n\n    if (rc == NGX_AGAIN) {\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    /* rc == NGX_ERROR || rc >= NGX_OK */\n\n    if (ctx->entered_content_phase) {\n        ngx_http_lua_finalize_request(r, rc);\n        return NGX_DONE;\n    }\n\n    return rc;\n}\n\n\nstatic void\nngx_http_lua_pipe_dummy_event_handler(ngx_event_t *ev)\n{\n    /* do nothing */\n}\n\n\nstatic void\nngx_http_lua_pipe_clear_event(ngx_event_t *ev)\n{\n    ev->handler = ngx_http_lua_pipe_dummy_event_handler;\n\n    if (ev->timer_set) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                       \"lua pipe del timer for ev:%p\", ev);\n        ngx_del_timer(ev);\n    }\n\n    if (ev->posted) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                       \"lua pipe del posted event for ev:%p\", ev);\n        ngx_delete_posted_event(ev);\n    }\n}\n\n\nstatic void\nngx_http_lua_pipe_proc_read_stdout_cleanup(void *data)\n{\n    ngx_event_t                    *rev;\n    ngx_connection_t               *c;\n    ngx_http_lua_co_ctx_t          *wait_co_ctx = data;\n    ngx_http_lua_ffi_pipe_proc_t   *proc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua pipe proc read stdout cleanup\");\n\n    proc = wait_co_ctx->data;\n\n    wait_co_ctx->cleanup = NULL;\n\n    if (proc->pipe == NULL) {\n        /* pipe_proc_destroy already ran (LIFO pool cleanup) and cancelled\n         * timers/connections; nothing left to clean up here. */\n        return;\n    }\n\n    c = proc->pipe->stdout_ctx->c;\n    if (c) {\n        rev = c->read;\n        ngx_http_lua_pipe_clear_event(rev);\n    }\n}\n\n\nstatic void\nngx_http_lua_pipe_proc_read_stderr_cleanup(void *data)\n{\n    ngx_event_t                    *rev;\n    ngx_connection_t               *c;\n    ngx_http_lua_co_ctx_t          *wait_co_ctx = data;\n    ngx_http_lua_ffi_pipe_proc_t   *proc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua pipe proc read stderr cleanup\");\n\n    proc = wait_co_ctx->data;\n\n    wait_co_ctx->cleanup = NULL;\n\n    if (proc->pipe == NULL) {\n        return;\n    }\n\n    c = proc->pipe->stderr_ctx->c;\n    if (c) {\n        rev = c->read;\n        ngx_http_lua_pipe_clear_event(rev);\n    }\n}\n\n\nstatic void\nngx_http_lua_pipe_proc_write_cleanup(void *data)\n{\n    ngx_event_t                    *wev;\n    ngx_connection_t               *c;\n    ngx_http_lua_co_ctx_t          *wait_co_ctx = data;\n    ngx_http_lua_ffi_pipe_proc_t   *proc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua pipe proc write cleanup\");\n\n    proc = wait_co_ctx->data;\n\n    wait_co_ctx->cleanup = NULL;\n\n    if (proc->pipe == NULL) {\n        return;\n    }\n\n    c = proc->pipe->stdin_ctx->c;\n    if (c) {\n        wev = c->write;\n        ngx_http_lua_pipe_clear_event(wev);\n    }\n}\n\n\nstatic void\nngx_http_lua_pipe_proc_wait_cleanup(void *data)\n{\n    ngx_rbtree_node_t              *node;\n    ngx_http_lua_co_ctx_t          *wait_co_ctx = data;\n    ngx_http_lua_pipe_node_t       *pipe_node;\n    ngx_http_lua_ffi_pipe_proc_t   *proc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua pipe proc wait cleanup\");\n\n    proc = wait_co_ctx->data;\n\n    wait_co_ctx->cleanup = NULL;\n\n    if (proc->pipe == NULL) {\n        return;\n    }\n\n    node = proc->pipe->node;\n    pipe_node = (ngx_http_lua_pipe_node_t *) &node->color;\n    pipe_node->wait_co_ctx = NULL;\n\n    ngx_http_lua_pipe_clear_event(&wait_co_ctx->sleep);\n}\n\n\n#endif /* HAVE_NGX_LUA_PIPE */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_pipe.h",
    "content": "\n/*\n * Copyright (C) by OpenResty Inc.\n */\n\n\n#ifndef _NGX_HTTP_LUA_PIPE_H_INCLUDED_\n#define _NGX_HTTP_LUA_PIPE_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\ntypedef ngx_int_t (*ngx_http_lua_pipe_input_filter)(void *data, ssize_t bytes);\n\n\ntypedef struct {\n    ngx_connection_t                   *c;\n    ngx_http_lua_pipe_input_filter      input_filter;\n    void                               *input_filter_ctx;\n    size_t                              rest;\n    ngx_chain_t                        *buf_in;\n    ngx_chain_t                        *bufs_in;\n    ngx_buf_t                           buffer;\n    ngx_err_t                           pipe_errno;\n    unsigned                            err_type:16;\n    unsigned                            eof:1;\n} ngx_http_lua_pipe_ctx_t;\n\n\ntypedef struct ngx_http_lua_pipe_s  ngx_http_lua_pipe_t;\n\n\ntypedef struct {\n    ngx_pid_t               _pid;\n    ngx_msec_t              write_timeout;\n    ngx_msec_t              stdout_read_timeout;\n    ngx_msec_t              stderr_read_timeout;\n    ngx_msec_t              wait_timeout;\n    /* pipe hides the implementation from the Lua binding */\n    ngx_http_lua_pipe_t    *pipe;\n} ngx_http_lua_ffi_pipe_proc_t;\n\n\ntypedef int (*ngx_http_lua_pipe_retval_handler)(\n    ngx_http_lua_ffi_pipe_proc_t *proc, lua_State *L);\n\n\nstruct ngx_http_lua_pipe_s {\n    ngx_pool_t                         *pool;\n    ngx_chain_t                        *free_bufs;\n    ngx_rbtree_node_t                  *node;\n    int                                 stdin_fd;\n    int                                 stdout_fd;\n    int                                 stderr_fd;\n    ngx_http_lua_pipe_ctx_t            *stdin_ctx;\n    ngx_http_lua_pipe_ctx_t            *stdout_ctx;\n    ngx_http_lua_pipe_ctx_t            *stderr_ctx;\n    ngx_http_lua_pipe_retval_handler    retval_handler;\n    ngx_http_cleanup_pt                *cleanup;\n    ngx_http_request_t                 *r;\n    size_t                              buffer_size;\n    unsigned                            closed:1;\n    unsigned                            dead:1;\n    unsigned                            timeout:1;\n    unsigned                            merge_stderr:1;\n};\n\n\ntypedef struct {\n    u_char                           color;\n    u_char                           reason_code;\n    int                              status;\n    ngx_http_lua_co_ctx_t           *wait_co_ctx;\n    ngx_http_lua_ffi_pipe_proc_t    *proc;\n} ngx_http_lua_pipe_node_t;\n\n\ntypedef struct {\n    int     signo;\n    char   *signame;\n} ngx_http_lua_pipe_signal_t;\n\n\n#if !(NGX_WIN32) && defined(HAVE_SOCKET_CLOEXEC_PATCH)\n#define HAVE_NGX_LUA_PIPE   1\n\n\nvoid ngx_http_lua_pipe_init(void);\nngx_int_t ngx_http_lua_pipe_add_signal_handler(ngx_cycle_t *cycle);\n#endif\n\n\n#endif /* _NGX_HTTP_LUA_PIPE_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_precontentby.c",
    "content": "\n/*\n * Copyright (C) Hanada <im@hanada.info>\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include <nginx.h>\n#include \"ngx_http_lua_precontentby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_exception.h\"\n#include \"ngx_http_lua_cache.h\"\n\n\nstatic ngx_int_t ngx_http_lua_precontent_by_chunk(lua_State *L,\n    ngx_http_request_t *r);\n\n\nngx_int_t\nngx_http_lua_precontent_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                   rc;\n    ngx_http_lua_ctx_t         *ctx;\n    ngx_http_lua_loc_conf_t    *llcf;\n    ngx_http_lua_main_conf_t   *lmcf;\n    ngx_http_phase_handler_t    tmp, *ph, *cur_ph, *last_ph;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua precontent handler, uri:\\\"%V\\\" c:%ud\", &r->uri,\n                   r->main->count);\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    if (!lmcf->postponed_to_precontent_phase_end) {\n\n        lmcf->postponed_to_precontent_phase_end = 1;\n\n        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n        ph = cmcf->phase_engine.handlers;\n        cur_ph = &ph[r->phase_handler];\n        last_ph = &ph[cur_ph->next - 1];\n\n        dd(\"ph cur: %d, ph next: %d\", (int) r->phase_handler,\n           (int) (cur_ph->next - 2));\n\n        if (cur_ph < last_ph) {\n            dd(\"swapping the contents of cur_ph and last_ph...\");\n\n            tmp = *cur_ph;\n\n            memmove(cur_ph, cur_ph + 1,\n                    (last_ph - cur_ph) * sizeof (ngx_http_phase_handler_t));\n\n            *last_ph = tmp;\n\n            r->phase_handler--; /* redo the current ph */\n\n            return NGX_DECLINED;\n        }\n    }\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->precontent_handler == NULL) {\n        dd(\"no precontent handler found\");\n        return NGX_DECLINED;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    dd(\"ctx = %p\", ctx);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    dd(\"entered? %d\", (int) ctx->entered_precontent_phase);\n\n\n    if (ctx->entered_precontent_phase) {\n        dd(\"precontentby: calling wev handler\");\n        rc = ctx->resume_handler(r);\n        dd(\"precontentby: wev handler returns %d\", (int) rc);\n\n        if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) {\n            return rc;\n        }\n\n        if (rc == NGX_OK) {\n            if (r->header_sent\n                || (r->headers_out.status != 0 && ctx->out != NULL))\n            {\n                dd(\"header already sent\");\n\n                /* response header was already generated in precontent_by_lua*,\n                 * so it is no longer safe to proceed to later phases\n                 * which may generate responses again */\n\n                if (!ctx->eof) {\n                    dd(\"eof not yet sent\");\n\n                    rc = ngx_http_lua_send_chain_link(r, ctx, NULL\n                                                     /* indicate last_buf */);\n                    if (rc == NGX_ERROR || rc > NGX_OK) {\n                        return rc;\n                    }\n                }\n\n                return NGX_HTTP_OK;\n            }\n\n            return NGX_OK;\n        }\n\n        return NGX_DECLINED;\n    }\n\n    if (ctx->waiting_more_body) {\n        dd(\"WAITING MORE BODY\");\n        return NGX_DONE;\n    }\n\n    if (llcf->force_read_body && !ctx->read_body_done) {\n        r->request_body_in_single_buf = 1;\n        r->request_body_in_persistent_file = 1;\n        r->request_body_in_clean_file = 1;\n\n        rc = ngx_http_read_client_request_body(r,\n                                       ngx_http_lua_generic_phase_post_read);\n\n        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n\n        if (rc == NGX_AGAIN) {\n            ctx->waiting_more_body = 1;\n            return NGX_DONE;\n        }\n    }\n\n    dd(\"calling precontent handler\");\n    return llcf->precontent_handler(r);\n}\n\n\nngx_int_t\nngx_http_lua_precontent_handler_inline(ngx_http_request_t *r)\n{\n    ngx_int_t                  rc;\n    lua_State                 *L;\n    ngx_http_lua_loc_conf_t   *llcf;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->precontent_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua inline script (w/ cache) sp = 1 */\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       llcf->precontent_src.value.data,\n                                       llcf->precontent_src.value.len,\n                                       &llcf->precontent_src_ref,\n                                       llcf->precontent_src_key,\n                                       (const char *)\n                                       llcf->precontent_chunkname);\n\n    if (rc != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    return ngx_http_lua_precontent_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_precontent_handler_file(ngx_http_request_t *r)\n{\n    u_char                    *script_path;\n    ngx_int_t                  rc;\n    ngx_str_t                  eval_src;\n    lua_State                 *L;\n    ngx_http_lua_loc_conf_t   *llcf;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    /* Eval nginx variables in code path string first */\n    if (ngx_http_complex_value(r, &llcf->precontent_src,\n                               &eval_src) != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,\n                                           eval_src.len);\n\n    if (script_path == NULL) {\n        return NGX_ERROR;\n    }\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->precontent_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua script file (w/ cache)        sp = 1 */\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,\n                                     &llcf->precontent_src_ref,\n                                     llcf->precontent_src_key);\n    if (rc != NGX_OK) {\n        if (rc < NGX_HTTP_SPECIAL_RESPONSE) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_precontent_by_chunk(L, r);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_precontent_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    int                  co_ref;\n    ngx_int_t            rc;\n    ngx_uint_t           nreqs;\n    lua_State           *co;\n    ngx_event_t         *rev;\n    ngx_connection_t    *c;\n    ngx_http_lua_ctx_t  *ctx;\n    ngx_pool_cleanup_t  *cln;\n\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    /*  {{{ new coroutine to handle request */\n    co = ngx_http_lua_new_thread(r, L, &co_ref);\n\n    if (co == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"lua: failed to create new coroutine \"\n                      \"to handle request\");\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    /*  move code closure to new coroutine */\n    lua_xmove(L, co, 1);\n\n#ifndef OPENRESTY_LUAJIT\n    /*  set closure's env table to new coroutine's globals table */\n    ngx_http_lua_get_globals_table(co);\n    lua_setfenv(co, -2);\n#endif\n\n    /*  save nginx request in coroutine globals table */\n    ngx_http_lua_set_req(co, r);\n\n    /*  {{{ initialize request context */\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    dd(\"ctx = %p\", ctx);\n\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_lua_reset_ctx(r, L, ctx);\n\n    ctx->entered_precontent_phase = 1;\n\n    ctx->cur_co_ctx = &ctx->entry_co_ctx;\n    ctx->cur_co_ctx->co = co;\n    ctx->cur_co_ctx->co_ref = co_ref;\n#ifdef NGX_LUA_USE_ASSERT\n    ctx->cur_co_ctx->co_top = 1;\n#endif\n\n    ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);\n\n    /*  }}} */\n\n    /*  {{{ register nginx pool cleanup hooks */\n    if (ctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n        if (cln == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        cln->handler = ngx_http_lua_request_cleanup_handler;\n        cln->data = ctx;\n        ctx->cleanup = &cln->handler;\n    }\n    /*  }}} */\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_PRECONTENT;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->check_client_abort) {\n        r->read_event_handler = ngx_http_lua_rd_check_broken_connection;\n\n#if (NGX_HTTP_V2)\n        if (!r->stream) {\n#endif\n\n        rev = r->connection->read;\n\n        if (!rev->active) {\n            if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n\n#if (NGX_HTTP_V2)\n        }\n#endif\n\n    } else {\n        r->read_event_handler = ngx_http_block_reading;\n    }\n\n    c = r->connection;\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(L, r, ctx, 0);\n\n    dd(\"returned %d\", (int) rc);\n\n    if (rc == NGX_ERROR || rc > NGX_OK) {\n        return rc;\n    }\n\n    if (rc == NGX_AGAIN) {\n        rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);\n\n        if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) {\n            return rc;\n        }\n\n        if (rc != NGX_OK) {\n            return NGX_DECLINED;\n        }\n\n    } else if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n\n        rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);\n\n        if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) {\n            return rc;\n        }\n\n        if (rc != NGX_OK) {\n            return NGX_DECLINED;\n        }\n    }\n\n#if 1\n    if (rc == NGX_OK) {\n        if (r->header_sent || (r->headers_out.status != 0 && ctx->out != NULL))\n        {\n            dd(\"header already sent\");\n\n            /* response header was already generated in precontent_by_lua*,\n             * so it is no longer safe to proceed to later phases\n             * which may generate responses again */\n\n            if (!ctx->eof) {\n                dd(\"eof not yet sent\");\n\n                rc = ngx_http_lua_send_chain_link(r, ctx, NULL\n                                                  /* indicate last_buf */);\n                if (rc == NGX_ERROR || rc > NGX_OK) {\n                    return rc;\n                }\n            }\n\n            return NGX_HTTP_OK;\n        }\n\n        return NGX_OK;\n    }\n#endif\n\n    return NGX_DECLINED;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_precontentby.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_PRECONTENTBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_PRECONTENTBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_precontent_handler(ngx_http_request_t *r);\nngx_int_t ngx_http_lua_precontent_handler_inline(ngx_http_request_t *r);\nngx_int_t ngx_http_lua_precontent_handler_file(ngx_http_request_t *r);\n\n\n#endif /* _NGX_HTTP_LUA_PRECONTENTBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_probe.h",
    "content": "/*\n * automatically generated from the file dtrace/ngx_lua_provider.d by the\n *  gen-dtrace-probe-header tool in the nginx-devel-utils project:\n *  https://github.com/agentzh/nginx-devel-utils\n */\n\n#ifndef _NGX_HTTP_LUA_PROBE_H_INCLUDED_\n#define _NGX_HTTP_LUA_PROBE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#if defined(NGX_DTRACE) && NGX_DTRACE\n\n#include <ngx_dtrace_provider.h>\n\n#define ngx_http_lua_probe_info(s)                                           \\\n    NGINX_LUA_HTTP_LUA_INFO(s)\n\n#define ngx_http_lua_probe_register_preload_package(L, pkg)                  \\\n    NGINX_LUA_HTTP_LUA_REGISTER_PRELOAD_PACKAGE(L, pkg)\n\n#define ngx_http_lua_probe_req_socket_consume_preread(r, data, len)          \\\n    NGINX_LUA_HTTP_LUA_REQ_SOCKET_CONSUME_PREREAD(r, data, len)\n\n#define ngx_http_lua_probe_user_coroutine_create(r, parent, child)           \\\n    NGINX_LUA_HTTP_LUA_USER_COROUTINE_CREATE(r, parent, child)\n\n#define ngx_http_lua_probe_user_coroutine_resume(r, parent, child)           \\\n    NGINX_LUA_HTTP_LUA_USER_COROUTINE_RESUME(r, parent, child)\n\n#define ngx_http_lua_probe_user_coroutine_yield(r, parent, child)            \\\n    NGINX_LUA_HTTP_LUA_USER_COROUTINE_YIELD(r, parent, child)\n\n#define ngx_http_lua_probe_thread_yield(r, L)                                \\\n    NGINX_LUA_HTTP_LUA_THREAD_YIELD(r, L)\n\n#define ngx_http_lua_probe_socket_tcp_send_start(r, u, data, len)            \\\n    NGINX_LUA_HTTP_LUA_SOCKET_TCP_SEND_START(r, u, data, len)\n\n#define ngx_http_lua_probe_socket_tcp_receive_done(r, u, data, len)          \\\n    NGINX_LUA_HTTP_LUA_SOCKET_TCP_RECEIVE_DONE(r, u, data, len)\n\n#define ngx_http_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, data,    \\\n                                                              len)           \\\n    NGINX_LUA_HTTP_LUA_SOCKET_TCP_SETKEEPALIVE_BUF_UNREAD(r, u, data, len)\n\n#define ngx_http_lua_probe_user_thread_spawn(r, creator, newthread)          \\\n    NGINX_LUA_HTTP_LUA_USER_THREAD_SPAWN(r, creator, newthread)\n\n#define ngx_http_lua_probe_thread_delete(r, thread, ctx)                     \\\n    NGINX_LUA_HTTP_LUA_THREAD_DELETE(r, thread, ctx)\n\n#define ngx_http_lua_probe_run_posted_thread(r, thread, status)              \\\n    NGINX_LUA_HTTP_LUA_RUN_POSTED_THREAD(r, thread, status)\n\n#define ngx_http_lua_probe_coroutine_done(r, co, success)                    \\\n    NGINX_LUA_HTTP_LUA_COROUTINE_DONE(r, co, success)\n\n#define ngx_http_lua_probe_user_thread_wait(parent, child)                   \\\n    NGINX_LUA_HTTP_LUA_USER_THREAD_WAIT(parent, child)\n\n#else /* !(NGX_DTRACE) */\n\n#define ngx_http_lua_probe_info(s)\n#define ngx_http_lua_probe_register_preload_package(L, pkg)\n#define ngx_http_lua_probe_req_socket_consume_preread(r, data, len)\n#define ngx_http_lua_probe_user_coroutine_create(r, parent, child)\n#define ngx_http_lua_probe_user_coroutine_resume(r, parent, child)\n#define ngx_http_lua_probe_user_coroutine_yield(r, parent, child)\n#define ngx_http_lua_probe_thread_yield(r, L)\n#define ngx_http_lua_probe_socket_tcp_send_start(r, u, data, len)\n#define ngx_http_lua_probe_socket_tcp_receive_done(r, u, data, len)\n#define ngx_http_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, data, len)\n#define ngx_http_lua_probe_user_thread_spawn(r, creator, newthread)\n#define ngx_http_lua_probe_thread_delete(r, thread, ctx)\n#define ngx_http_lua_probe_run_posted_thread(r, thread, status)\n#define ngx_http_lua_probe_coroutine_done(r, co, success)\n#define ngx_http_lua_probe_user_thread_wait(parent, child)\n\n#endif\n\n#endif /* _NGX_HTTP_LUA_PROBE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/ngx_http_lua_proxy_ssl_certby.c",
    "content": "/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n\n#include \"ddebug.h\"\n#include \"ngx_http_lua_proxy_ssl_certby.h\"\n\n\n#if HAVE_LUA_PROXY_SSL\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_initworkerby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_ssl_module.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_directive.h\"\n#include \"ngx_http_lua_ssl.h\"\n\n\n\nstatic void ngx_http_lua_proxy_ssl_cert_done(void *data);\nstatic void ngx_http_lua_proxy_ssl_cert_aborted(void *data);\nstatic ngx_int_t ngx_http_lua_proxy_ssl_cert_by_chunk(lua_State *L,\n    ngx_http_request_t *r);\n\n\nngx_int_t\nngx_http_lua_proxy_ssl_cert_set_callback(ngx_conf_t *cf)\n{\n    void                      *plcf;\n    ngx_http_upstream_conf_t  *ucf;\n    ngx_ssl_t                 *ssl;\n\n    /*\n     * Nginx doesn't export ngx_http_proxy_loc_conf_t, so we can't directly\n     * get plcf here, but the first member of plcf is ngx_http_upstream_conf_t\n     */\n    plcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_proxy_module);\n    ucf = plcf;\n\n    ssl = ucf->ssl;\n\n    if (!ssl->ctx) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"proxy_ssl_certificate_by_lua* should be used with \"\n                      \"proxy_pass https url\");\n\n        return NGX_ERROR;\n    }\n\n    SSL_CTX_set_cert_cb(ssl->ctx, ngx_http_lua_proxy_ssl_cert_handler, NULL);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_lua_proxy_ssl_cert_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_loc_conf_t *llcf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L,\n                                     llcf->proxy_ssl_cert_src.data,\n                                     &llcf->proxy_ssl_cert_src_ref,\n                                     llcf->proxy_ssl_cert_src_key);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_proxy_ssl_cert_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_proxy_ssl_cert_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_loc_conf_t *llcf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       llcf->proxy_ssl_cert_src.data,\n                                       llcf->proxy_ssl_cert_src.len,\n                                       &llcf->proxy_ssl_cert_src_ref,\n                                       llcf->proxy_ssl_cert_src_key,\n                           (const char *) llcf->proxy_ssl_cert_chunkname);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_proxy_ssl_cert_by_chunk(L, r);\n}\n\n\nchar *\nngx_http_lua_proxy_ssl_cert_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_proxy_ssl_cert_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_proxy_ssl_cert_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    size_t                       chunkname_len;\n    u_char                      *chunkname;\n    u_char                      *cache_key = NULL;\n    u_char                      *name;\n    ngx_str_t                   *value;\n    ngx_http_lua_loc_conf_t     *llcf = conf;\n\n    /*  must specify a concrete handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (llcf->proxy_ssl_cert_handler) {\n        return \"is duplicate\";\n    }\n\n    if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    llcf->proxy_ssl_cert_handler =\n        (ngx_http_lua_loc_conf_handler_pt) cmd->post;\n\n    if (cmd->post == ngx_http_lua_proxy_ssl_cert_handler_file) {\n        /* Lua code in an external file */\n\n        name = ngx_http_lua_rebase_path(cf->pool, value[1].data,\n                                        value[1].len);\n        if (name == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                    value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        llcf->proxy_ssl_cert_src.data = name;\n        llcf->proxy_ssl_cert_src.len = ngx_strlen(name);\n\n    } else {\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf,\n                                                \"proxy_ssl_certificate_by_lua\",\n                                                value[1].data,\n                                                value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        chunkname = ngx_http_lua_gen_chunk_name(cf,\n                                                \"proxy_ssl_certificate_by_lua\",\n                                    sizeof(\"proxy_ssl_certificate_by_lua\") - 1,\n                                    &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        llcf->proxy_ssl_cert_src = value[1];\n        llcf->proxy_ssl_cert_chunkname = chunkname;\n    }\n\n    llcf->proxy_ssl_cert_src_key = cache_key;\n\n    return NGX_CONF_OK;\n}\n\n\nint\nngx_http_lua_proxy_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data)\n{\n    lua_State                       *L;\n    ngx_int_t                        rc;\n    ngx_connection_t                *c;\n    ngx_http_request_t              *r = NULL;\n    ngx_pool_cleanup_t              *cln;\n    ngx_http_lua_loc_conf_t         *llcf;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n\n    c = ngx_ssl_get_connection(ssl_conn);  /* upstream connection */\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"proxy ssl cert: connection reusable: %ud\", c->reusable);\n\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n\n    dd(\"proxy ssl cert handler, cert-ctx=%p\", cctx);\n\n    if (cctx && cctx->entered_proxy_ssl_cert_handler) {\n        /* not the first time */\n\n        if (cctx->done) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"proxy_ssl_certificate_by_lua: \"\n                           \"cert cb exit code: %d\",\n                           cctx->exit_code);\n\n            dd(\"lua proxy ssl cert done, finally\");\n            return cctx->exit_code;\n        }\n\n        return -1;\n    }\n\n    dd(\"first time\");\n\n#if (nginx_version < 1017009)\n    ngx_reusable_connection(c, 0);\n#endif\n\n    r = c->data;\n\n    if (cctx == NULL) {\n        cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t));\n        if (cctx == NULL) {\n            goto failed;  /* error */\n        }\n\n        cctx->ctx_ref = LUA_NOREF;\n    }\n\n    cctx->connection = c;\n    cctx->request = r;\n    cctx->exit_code = 1;  /* successful by default */\n    cctx->original_request_count = r->main->count;\n    cctx->done = 0;\n    cctx->entered_proxy_ssl_cert_handler = 1;\n    cctx->pool = ngx_create_pool(128, c->log);\n    if (cctx->pool == NULL) {\n        goto failed;\n    }\n\n    dd(\"setting cctx\");\n\n    if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index,\n                        cctx) == 0)\n    {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"SSL_set_ex_data() failed\");\n        goto failed;\n    }\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    /* TODO honor lua_code_cache off */\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    c->log->action = \"loading proxy ssl certificate by lua\";\n\n    rc = llcf->proxy_ssl_cert_handler(r, llcf, L);\n\n    if (rc >= NGX_OK || rc == NGX_ERROR) {\n        cctx->done = 1;\n\n        if (cctx->cleanup) {\n            *cctx->cleanup = NULL;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"proxy_ssl_certificate_by_lua: \"\n                       \"handler return value: %i, cert cb exit code: %d\",\n                       rc, cctx->exit_code);\n\n        c->log->action = \"proxy pass SSL handshaking\";\n        return cctx->exit_code;\n    }\n\n    /* rc == NGX_DONE */\n\n    cln = ngx_pool_cleanup_add(cctx->pool, 0);\n    if (cln == NULL) {\n        goto failed;\n    }\n\n    cln->handler = ngx_http_lua_proxy_ssl_cert_done;\n    cln->data = cctx;\n\n    if (cctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(c->pool, 0);\n        if (cln == NULL) {\n            goto failed;\n        }\n\n        cln->data = cctx;\n        cctx->cleanup = &cln->handler;\n    }\n\n    *cctx->cleanup = ngx_http_lua_proxy_ssl_cert_aborted;\n\n    return -1;\n\nfailed:\n\n    if (cctx && cctx->pool) {\n        ngx_destroy_pool(cctx->pool);\n    }\n\n    return 0;\n}\n\n\nstatic void\nngx_http_lua_proxy_ssl_cert_done(void *data)\n{\n    ngx_connection_t        *c;\n    ngx_http_lua_ssl_ctx_t  *cctx = data;\n\n    dd(\"lua proxy ssl cert done\");\n\n    if (cctx->aborted) {\n        return;\n    }\n\n    ngx_http_lua_assert(cctx->done == 0);\n\n    cctx->done = 1;\n\n    if (cctx->cleanup) {\n        *cctx->cleanup = NULL;\n    }\n\n    c = cctx->connection;\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    if (c->write->timer_set) {\n        ngx_del_timer(c->write);\n    }\n\n    c->log->action = \"proxy pass SSL handshaking\";\n\n    ngx_post_event(c->write, &ngx_posted_events);\n}\n\n\nstatic void\nngx_http_lua_proxy_ssl_cert_aborted(void *data)\n{\n    ngx_http_lua_ssl_ctx_t  *cctx = data;\n\n    dd(\"lua proxy ssl cert aborted\");\n\n    if (cctx->done) {\n        /* completed successfully already */\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0,\n                   \"proxy_ssl_certificate_by_lua: cert cb aborted\");\n\n    cctx->aborted = 1;\n    cctx->connection->ssl = NULL;\n    cctx->exit_code = 0;\n    if (cctx->pool) {\n        ngx_destroy_pool(cctx->pool);\n        cctx->pool = NULL;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_lua_proxy_ssl_cert_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    int                      co_ref;\n    ngx_int_t                rc;\n    lua_State               *co;\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_pool_cleanup_t      *cln;\n    ngx_http_upstream_t     *u;\n    ngx_connection_t        *c;\n    ngx_http_lua_ssl_ctx_t  *cctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            rc = NGX_ERROR;\n            ngx_http_lua_finalize_request(r, rc);\n            return rc;\n        }\n\n    } else {\n        dd(\"reset ctx\");\n        ngx_http_lua_reset_ctx(r, L, ctx);\n    }\n\n    ctx->entered_content_phase = 1;\n\n    /*  {{{ new coroutine to handle request */\n    co = ngx_http_lua_new_thread(r, L, &co_ref);\n\n    if (co == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"lua: failed to create new coroutine to handle request\");\n\n        rc = NGX_ERROR;\n        ngx_http_lua_finalize_request(r, rc);\n        return rc;\n    }\n\n    /*  move code closure to new coroutine */\n    lua_xmove(L, co, 1);\n\n#ifndef OPENRESTY_LUAJIT\n    /*  set closure's env table to new coroutine's globals table */\n    ngx_http_lua_get_globals_table(co);\n    lua_setfenv(co, -2);\n#endif\n\n    /* save nginx request in coroutine globals table */\n    ngx_http_lua_set_req(co, r);\n\n    ctx->cur_co_ctx = &ctx->entry_co_ctx;\n    ctx->cur_co_ctx->co = co;\n    ctx->cur_co_ctx->co_ref = co_ref;\n#ifdef NGX_LUA_USE_ASSERT\n    ctx->cur_co_ctx->co_top = 1;\n#endif\n\n    ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);\n\n    /* register request cleanup hooks */\n    if (ctx->cleanup == NULL) {\n        u = r->upstream;\n        c = u->peer.connection;\n        cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n\n        cln = ngx_pool_cleanup_add(cctx->pool, 0);\n        if (cln == NULL) {\n            rc = NGX_ERROR;\n            ngx_http_lua_finalize_request(r, rc);\n            return rc;\n        }\n\n        cln->handler = ngx_http_lua_request_cleanup_handler;\n        cln->data = ctx;\n        ctx->cleanup = &cln->handler;\n    }\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT;\n\n    rc = ngx_http_lua_run_thread(L, r, ctx, 0);\n\n    if (rc == NGX_ERROR || rc >= NGX_OK) {\n        /* do nothing */\n\n    } else if (rc == NGX_AGAIN) {\n        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0);\n\n    } else if (rc == NGX_DONE) {\n        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 1);\n\n    } else {\n        rc = NGX_OK;\n    }\n\n    ngx_http_lua_finalize_request(r, rc);\n    return rc;\n}\n\n\nint\nngx_http_lua_ffi_proxy_ssl_get_tls1_version(ngx_http_request_t *r, char **err)\n{\n    ngx_http_upstream_t             *u;\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n\n    u = r->upstream;\n    if (u == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    c = u->peer.connection;\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad upstream connection\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    dd(\"tls1 ver: %d\", SSL_version(ssl_conn));\n\n    return SSL_version(ssl_conn);\n}\n\n\nint\nngx_http_lua_ffi_proxy_ssl_clear_certs(ngx_http_request_t *r, char **err)\n{\n    ngx_http_upstream_t             *u;\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n\n    u = r->upstream;\n    if (u == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    c = u->peer.connection;\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad upstream connection\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    SSL_certs_clear(ssl_conn);\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_proxy_ssl_set_der_certificate(ngx_http_request_t *r,\n    const char *data, size_t len, char **err)\n{\n    ngx_http_upstream_t             *u;\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n    BIO                             *bio = NULL;\n    X509                            *x509 = NULL;\n\n    u = r->upstream;\n    if (u == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    c = u->peer.connection;\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad upstream connection\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    bio = BIO_new_mem_buf((char *) data, len);\n    if (bio == NULL) {\n        *err = \"BIO_new_mem_buf() failed\";\n        goto failed;\n    }\n\n    x509 = d2i_X509_bio(bio, NULL);\n    if (x509 == NULL) {\n        *err = \"d2i_X509_bio() failed\";\n        goto failed;\n    }\n\n    if (SSL_use_certificate(ssl_conn, x509) == 0) {\n        *err = \"SSL_use_certificate() failed\";\n        goto failed;\n    }\n\n    X509_free(x509);\n    x509 = NULL;\n\n    /* read rest of the chain */\n\n    while (!BIO_eof(bio)) {\n\n        x509 = d2i_X509_bio(bio, NULL);\n        if (x509 == NULL) {\n            *err = \"d2i_X509_bio() failed\";\n            goto failed;\n        }\n\n        if (SSL_add0_chain_cert(ssl_conn, x509) == 0) {\n            *err = \"SSL_add0_chain_cert() failed\";\n            goto failed;\n        }\n    }\n\n    BIO_free(bio);\n\n    *err = NULL;\n    return NGX_OK;\n\nfailed:\n\n    if (bio) {\n        BIO_free(bio);\n    }\n\n    if (x509) {\n        X509_free(x509);\n    }\n\n    ERR_clear_error();\n\n    return NGX_ERROR;\n}\n\n\nint\nngx_http_lua_ffi_proxy_ssl_set_der_private_key(ngx_http_request_t *r,\n    const char *data, size_t len, char **err)\n{\n    ngx_http_upstream_t             *u;\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n    BIO                             *bio = NULL;\n    EVP_PKEY                        *pkey = NULL;\n\n    u = r->upstream;\n    if (u == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    c = u->peer.connection;\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad upstream connection\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    bio = BIO_new_mem_buf((char *) data, len);\n    if (bio == NULL) {\n        *err = \"BIO_new_mem_buf() failed\";\n        goto failed;\n    }\n\n    pkey = d2i_PrivateKey_bio(bio, NULL);\n    if (pkey == NULL) {\n        *err = \"d2i_PrivateKey_bio() failed\";\n        goto failed;\n    }\n\n    if (SSL_use_PrivateKey(ssl_conn, pkey) == 0) {\n        *err = \"SSL_use_PrivateKey() failed\";\n        goto failed;\n    }\n\n    EVP_PKEY_free(pkey);\n    BIO_free(bio);\n\n    return NGX_OK;\n\nfailed:\n\n    if (pkey) {\n        EVP_PKEY_free(pkey);\n    }\n\n    if (bio) {\n        BIO_free(bio);\n    }\n\n    ERR_clear_error();\n\n    return NGX_ERROR;\n}\n\n\nint\nngx_http_lua_ffi_proxy_ssl_set_cert(ngx_http_request_t *r,\n    void *cdata, char **err)\n{\n#ifdef OPENSSL_IS_BORINGSSL\n    size_t             i;\n#else\n    int                i;\n#endif\n    ngx_http_upstream_t             *u;\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n    X509                            *x509 = NULL;\n    STACK_OF(X509)                  *chain = cdata;\n\n    u = r->upstream;\n    if (u == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    c = u->peer.connection;\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad upstream connection\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    if (sk_X509_num(chain) < 1) {\n        *err = \"invalid certificate chain\";\n        goto failed;\n    }\n\n    x509 = sk_X509_value(chain, 0);\n    if (x509 == NULL) {\n        *err = \"sk_X509_value() failed\";\n        goto failed;\n    }\n\n    if (SSL_use_certificate(ssl_conn, x509) == 0) {\n        *err = \"SSL_use_certificate() failed\";\n        goto failed;\n    }\n\n    x509 = NULL;\n\n    /* read rest of the chain */\n\n    for (i = 1; i < sk_X509_num(chain); i++) {\n\n        x509 = sk_X509_value(chain, i);\n        if (x509 == NULL) {\n            *err = \"sk_X509_value() failed\";\n            goto failed;\n        }\n\n        if (SSL_add1_chain_cert(ssl_conn, x509) == 0) {\n            *err = \"SSL_add1_chain_cert() failed\";\n            goto failed;\n        }\n    }\n\n    *err = NULL;\n    return NGX_OK;\n\nfailed:\n\n    ERR_clear_error();\n\n    return NGX_ERROR;\n}\n\n\nint\nngx_http_lua_ffi_proxy_ssl_set_priv_key(ngx_http_request_t *r,\n    void *cdata, char **err)\n{\n    ngx_http_upstream_t             *u;\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n    EVP_PKEY                        *pkey = NULL;\n\n    u = r->upstream;\n    if (u == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    c = u->peer.connection;\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad upstream connection\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    pkey = cdata;\n    if (pkey == NULL) {\n        *err = \"invalid private key\";\n        goto failed;\n    }\n\n    if (SSL_use_PrivateKey(ssl_conn, pkey) == 0) {\n        *err = \"SSL_use_PrivateKey() failed\";\n        goto failed;\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    ERR_clear_error();\n\n    return NGX_ERROR;\n}\n\n#endif /* HAVE_LUA_PROXY_SSL */\n"
  },
  {
    "path": "src/ngx_http_lua_proxy_ssl_certby.h",
    "content": "/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n#ifndef _NGX_HTTP_LUA_PROXY_SSL_CERTBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_PROXY_SSL_CERTBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n#if HAVE_LUA_PROXY_SSL\n\n/* do not introduce ngx_http_proxy_module to pollute ngx_http_lua_module.c */\nextern ngx_module_t  ngx_http_proxy_module;\n\nngx_int_t ngx_http_lua_proxy_ssl_cert_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_loc_conf_t *llcf, lua_State *L);\n\nngx_int_t ngx_http_lua_proxy_ssl_cert_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_loc_conf_t *llcf, lua_State *L);\n\nchar *ngx_http_lua_proxy_ssl_cert_by_lua_block(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\n\nchar *ngx_http_lua_proxy_ssl_cert_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nint ngx_http_lua_proxy_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data);\n\nngx_int_t ngx_http_lua_proxy_ssl_cert_set_callback(ngx_conf_t *cf);\n\n#endif  /* HAVE_LUA_PROXY_SSL */\n\n\n#endif /* _NGX_HTTP_LUA_PROXY_SSL_CERTBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_proxy_ssl_verifyby.c",
    "content": "/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n\n#include \"ddebug.h\"\n#include \"ngx_http_lua_proxy_ssl_verifyby.h\"\n\n\n#if HAVE_LUA_PROXY_SSL\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_initworkerby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_ssl_module.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_directive.h\"\n#include \"ngx_http_lua_ssl.h\"\n\n\nstatic void ngx_http_lua_proxy_ssl_verify_done(void *data);\nstatic void ngx_http_lua_proxy_ssl_verify_aborted(void *data);\nstatic ngx_int_t ngx_http_lua_proxy_ssl_verify_by_chunk(lua_State *L,\n    ngx_http_request_t *r);\n\n\nngx_int_t\nngx_http_lua_proxy_ssl_verify_set_callback(ngx_conf_t *cf)\n{\n    void                      *plcf;\n    ngx_http_upstream_conf_t  *ucf;\n    ngx_ssl_t                 *ssl;\n\n    /*\n     * Nginx doesn't export ngx_http_proxy_loc_conf_t, so we can't directly\n     * get plcf here, but the first member of plcf is ngx_http_upstream_conf_t\n     */\n    plcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_proxy_module);\n    ucf = plcf;\n\n    ssl = ucf->ssl;\n\n    if (!ssl->ctx) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0, \"proxy_ssl_verify_by_lua* \"\n                      \"should be used with proxy_pass https url\");\n\n        return NGX_ERROR;\n    }\n\n    SSL_CTX_set_cert_verify_callback(ssl->ctx,\n                                     ngx_http_lua_proxy_ssl_verify_handler,\n                                     NULL);\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_lua_proxy_ssl_verify_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_loc_conf_t *llcf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L,\n                                     llcf->proxy_ssl_verify_src.data,\n                                     &llcf->proxy_ssl_verify_src_ref,\n                                     llcf->proxy_ssl_verify_src_key);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_proxy_ssl_verify_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_proxy_ssl_verify_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_loc_conf_t *llcf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       llcf->proxy_ssl_verify_src.data,\n                                       llcf->proxy_ssl_verify_src.len,\n                                       &llcf->proxy_ssl_verify_src_ref,\n                                       llcf->proxy_ssl_verify_src_key,\n                           (const char *) llcf->proxy_ssl_verify_chunkname);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_proxy_ssl_verify_by_chunk(L, r);\n}\n\n\nchar *\nngx_http_lua_proxy_ssl_verify_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_proxy_ssl_verify_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_proxy_ssl_verify_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    size_t                       chunkname_len;\n    u_char                      *chunkname;\n    u_char                      *cache_key = NULL;\n    u_char                      *name;\n    ngx_str_t                   *value;\n    ngx_http_lua_loc_conf_t     *llcf = conf;\n\n    /*  must specify a concrete handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (llcf->proxy_ssl_verify_handler) {\n        return \"is duplicate\";\n    }\n\n    if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    llcf->proxy_ssl_verify_handler =\n        (ngx_http_lua_loc_conf_handler_pt) cmd->post;\n\n    if (cmd->post == ngx_http_lua_proxy_ssl_verify_handler_file) {\n        /* Lua code in an external file */\n\n        name = ngx_http_lua_rebase_path(cf->pool, value[1].data,\n                                        value[1].len);\n        if (name == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                    value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        llcf->proxy_ssl_verify_src.data = name;\n        llcf->proxy_ssl_verify_src.len = ngx_strlen(name);\n\n    } else {\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf,\n                                                     \"proxy_ssl_verify_by_lua\",\n                                                     value[1].data,\n                                                     value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"proxy_ssl_verify_by_lua\",\n                                          sizeof(\"proxy_ssl_verify_by_lua\") - 1,\n                                          &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        llcf->proxy_ssl_verify_src = value[1];\n        llcf->proxy_ssl_verify_chunkname = chunkname;\n    }\n\n    llcf->proxy_ssl_verify_src_key = cache_key;\n\n    return NGX_CONF_OK;\n}\n\n\nint\nngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg)\n{\n    lua_State                       *L;\n    ngx_int_t                        rc;\n    ngx_connection_t                *c;\n    ngx_http_request_t              *r = NULL;\n    ngx_pool_cleanup_t              *cln;\n    ngx_http_lua_loc_conf_t         *llcf;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n    ngx_ssl_conn_t                  *ssl_conn;\n\n    ssl_conn = X509_STORE_CTX_get_ex_data(x509_store,\n                                          SSL_get_ex_data_X509_STORE_CTX_idx());\n\n    c = ngx_ssl_get_connection(ssl_conn);  /* upstream connection */\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"proxy ssl verify: connection reusable: %ud\", c->reusable);\n\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n\n    dd(\"proxy ssl verify handler, cert-verify-ctx=%p\", cctx);\n\n    if (cctx && cctx->entered_proxy_ssl_verify_handler) {\n        /* not the first time */\n\n        if (cctx->done) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"proxy_ssl_verify_by_lua: \"\n                           \"cert verify callback exit code: %d\",\n                           cctx->exit_code);\n\n            dd(\"lua proxy ssl verify done, finally\");\n            return cctx->exit_code;\n        }\n\n        return SSL_set_retry_verify(ssl_conn);\n    }\n\n    dd(\"first time\");\n\n#if (nginx_version < 1017009)\n    ngx_reusable_connection(c, 0);\n#endif\n\n    r = c->data;\n\n    if (cctx == NULL) {\n        cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t));\n        if (cctx == NULL) {\n            goto failed;  /* error */\n        }\n\n        cctx->ctx_ref = LUA_NOREF;\n    }\n\n    cctx->connection = c;\n    cctx->request = r;\n    cctx->x509_store = x509_store;\n    cctx->exit_code = 1;  /* successful by default */\n    cctx->original_request_count = r->main->count;\n    cctx->done = 0;\n    cctx->entered_proxy_ssl_verify_handler = 1;\n    cctx->pool = ngx_create_pool(128, c->log);\n    if (cctx->pool == NULL) {\n        goto failed;\n    }\n\n    dd(\"setting cctx\");\n\n    if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index,\n                        cctx) == 0)\n    {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"SSL_set_ex_data() failed\");\n        goto failed;\n    }\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n    if (llcf->upstream_skip_openssl_default_verify == 0) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"proxy_ssl_verify_by_lua: openssl default verify\");\n\n        rc = X509_verify_cert(x509_store);\n        if (rc == 0) {\n            goto failed;\n        }\n    }\n\n    /* TODO honor lua_code_cache off */\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    c->log->action = \"loading proxy ssl verify by lua\";\n\n    rc = llcf->proxy_ssl_verify_handler(r, llcf, L);\n\n    if (rc >= NGX_OK || rc == NGX_ERROR) {\n        cctx->done = 1;\n\n        if (cctx->cleanup) {\n            *cctx->cleanup = NULL;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"proxy_ssl_verify_by_lua: handler return value: %i, \"\n                       \"cert verify callback exit code: %d\", rc,\n                       cctx->exit_code);\n\n        c->log->action = \"proxy pass SSL handshaking\";\n        return cctx->exit_code;\n    }\n\n    /* rc == NGX_DONE */\n\n    cln = ngx_pool_cleanup_add(cctx->pool, 0);\n    if (cln == NULL) {\n        goto failed;\n    }\n\n    cln->handler = ngx_http_lua_proxy_ssl_verify_done;\n    cln->data = cctx;\n\n    if (cctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(c->pool, 0);\n        if (cln == NULL) {\n            goto failed;\n        }\n\n        cln->data = cctx;\n        cctx->cleanup = &cln->handler;\n    }\n\n    *cctx->cleanup = ngx_http_lua_proxy_ssl_verify_aborted;\n\n    return SSL_set_retry_verify(ssl_conn);\n\nfailed:\n\n    if (cctx && cctx->pool) {\n        ngx_destroy_pool(cctx->pool);\n    }\n\n    return 0;  /* verify failure or error */\n}\n\n\nstatic void\nngx_http_lua_proxy_ssl_verify_done(void *data)\n{\n    ngx_connection_t        *c;\n    ngx_http_lua_ssl_ctx_t  *cctx = data;\n\n    dd(\"lua proxy ssl verify done\");\n\n    if (cctx->aborted) {\n        return;\n    }\n\n    ngx_http_lua_assert(cctx->done == 0);\n\n    cctx->done = 1;\n\n    if (cctx->cleanup) {\n        *cctx->cleanup = NULL;\n    }\n\n    c = cctx->connection;\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    if (c->write->timer_set) {\n        ngx_del_timer(c->write);\n    }\n\n    c->log->action = \"proxy pass SSL handshaking\";\n\n    ngx_post_event(c->write, &ngx_posted_events);\n}\n\n\nstatic void\nngx_http_lua_proxy_ssl_verify_aborted(void *data)\n{\n    ngx_http_lua_ssl_ctx_t  *cctx = data;\n\n    dd(\"lua proxy ssl verify aborted\");\n\n    if (cctx->done) {\n        /* completed successfully already */\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0,\n                   \"proxy_ssl_verify_by_lua: cert verify callback aborted\");\n\n    cctx->aborted = 1;\n    cctx->connection->ssl = NULL;\n    cctx->exit_code = 0;\n    if (cctx->pool) {\n        ngx_destroy_pool(cctx->pool);\n        cctx->pool = NULL;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_lua_proxy_ssl_verify_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    int                      co_ref;\n    ngx_int_t                rc;\n    lua_State               *co;\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_pool_cleanup_t      *cln;\n    ngx_http_upstream_t     *u;\n    ngx_connection_t        *c;\n    ngx_http_lua_ssl_ctx_t  *cctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            rc = NGX_ERROR;\n            ngx_http_lua_finalize_request(r, rc);\n            return rc;\n        }\n\n    } else {\n        dd(\"reset ctx\");\n        ngx_http_lua_reset_ctx(r, L, ctx);\n    }\n\n    ctx->entered_content_phase = 1;\n\n    /*  {{{ new coroutine to handle request */\n    co = ngx_http_lua_new_thread(r, L, &co_ref);\n\n    if (co == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"lua: failed to create new coroutine to handle request\");\n\n        rc = NGX_ERROR;\n        ngx_http_lua_finalize_request(r, rc);\n        return rc;\n    }\n\n    /*  move code closure to new coroutine */\n    lua_xmove(L, co, 1);\n\n#ifndef OPENRESTY_LUAJIT\n    /*  set closure's env table to new coroutine's globals table */\n    ngx_http_lua_get_globals_table(co);\n    lua_setfenv(co, -2);\n#endif\n\n    /* save nginx request in coroutine globals table */\n    ngx_http_lua_set_req(co, r);\n\n    ctx->cur_co_ctx = &ctx->entry_co_ctx;\n    ctx->cur_co_ctx->co = co;\n    ctx->cur_co_ctx->co_ref = co_ref;\n#ifdef NGX_LUA_USE_ASSERT\n    ctx->cur_co_ctx->co_top = 1;\n#endif\n\n    ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);\n\n    /* register request cleanup hooks */\n    if (ctx->cleanup == NULL) {\n        u = r->upstream;\n        c = u->peer.connection;\n        cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n\n        cln = ngx_pool_cleanup_add(cctx->pool, 0);\n        if (cln == NULL) {\n            rc = NGX_ERROR;\n            ngx_http_lua_finalize_request(r, rc);\n            return rc;\n        }\n\n        cln->handler = ngx_http_lua_request_cleanup_handler;\n        cln->data = ctx;\n        ctx->cleanup = &cln->handler;\n    }\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY;\n\n    rc = ngx_http_lua_run_thread(L, r, ctx, 0);\n\n    if (rc == NGX_ERROR || rc >= NGX_OK) {\n        /* do nothing */\n\n    } else if (rc == NGX_AGAIN) {\n        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0);\n\n    } else if (rc == NGX_DONE) {\n        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 1);\n\n    } else {\n        rc = NGX_OK;\n    }\n\n    ngx_http_lua_finalize_request(r, rc);\n    return rc;\n}\n\n\n/*\n * openssl's doc of SSL_CTX_set_cert_verify_callback:\n * In any case a viable verification result value must\n * be reflected in the error member of x509_store_ctx,\n * which can be done using X509_STORE_CTX_set_error.\n */\nint\nngx_http_lua_ffi_proxy_ssl_set_verify_result(ngx_http_request_t *r,\n    int verify_result, char **err)\n{\n    ngx_http_upstream_t             *u;\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n    X509_STORE_CTX                  *x509_store;\n\n    u = r->upstream;\n    if (u == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    c = u->peer.connection;\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad upstream connection\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    dd(\"get cctx session\");\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n    if (cctx == NULL) {\n        *err = \"bad lua context\";\n        return NGX_ERROR;\n    }\n\n    x509_store = cctx->x509_store;\n\n    X509_STORE_CTX_set_error(x509_store, verify_result);\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_proxy_ssl_get_verify_result(ngx_http_request_t *r, char **err)\n{\n    ngx_http_upstream_t             *u;\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n    X509_STORE_CTX                  *x509_store;\n\n    u = r->upstream;\n    if (u == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    c = u->peer.connection;\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad upstream connection\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    dd(\"get cctx session\");\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n    if (cctx == NULL) {\n        *err = \"bad lua context\";\n        return NGX_ERROR;\n    }\n\n    x509_store = cctx->x509_store;\n\n    return X509_STORE_CTX_get_error(x509_store);\n}\n\n\nvoid\nngx_http_lua_ffi_proxy_ssl_free_verify_cert(void *cdata)\n{\n    X509  *cert = cdata;\n\n    X509_free(cert);\n}\n\n\nvoid *\nngx_http_lua_ffi_proxy_ssl_get_verify_cert(ngx_http_request_t *r, char **err)\n{\n    ngx_http_upstream_t             *u;\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n    X509_STORE_CTX                  *x509_store;\n    X509                            *x509;\n\n    u = r->upstream;\n    if (u == NULL) {\n        *err = \"bad request\";\n        return NULL;\n    }\n\n    c = u->peer.connection;\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad upstream connection\";\n        return NULL;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NULL;\n    }\n\n    dd(\"get cctx session\");\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n    if (cctx == NULL) {\n        *err = \"bad lua context\";\n        return NULL;\n    }\n\n    x509_store = cctx->x509_store;\n\n    x509 = X509_STORE_CTX_get0_cert(x509_store);\n\n    if (!X509_up_ref(x509)) {\n        *err = \"get verify result failed\";\n        return NULL;\n    }\n\n    return x509;\n}\n\n#endif /* HAVE_LUA_PROXY_SSL */\n"
  },
  {
    "path": "src/ngx_http_lua_proxy_ssl_verifyby.h",
    "content": "/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n#ifndef _NGX_HTTP_LUA_PROXY_SSL_VERIFYBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_PROXY_SSL_VERIFYBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\n#if (NGX_HTTP_SSL)\n#if HAVE_LUA_PROXY_SSL\n\n\n/* do not introduce ngx_http_proxy_module to pollute ngx_http_lua_module.c */\nextern ngx_module_t  ngx_http_proxy_module;\n\nngx_int_t ngx_http_lua_proxy_ssl_verify_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_loc_conf_t *llcf, lua_State *L);\n\nngx_int_t ngx_http_lua_proxy_ssl_verify_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_loc_conf_t *llcf, lua_State *L);\n\nchar *ngx_http_lua_proxy_ssl_verify_by_lua_block(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\n\nchar *ngx_http_lua_proxy_ssl_verify_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nint ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store,\n    void *arg);\n\nngx_int_t ngx_http_lua_proxy_ssl_verify_set_callback(ngx_conf_t *cf);\n\n#endif  /* HAVE_LUA_PROXY_SSL */\n#endif  /* NGX_HTTP_SSL */\n\n\n#endif /* _NGX_HTTP_LUA_PROXY_SSL_VERIFYBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_regex.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n#if (NGX_PCRE)\n\n#include \"ngx_http_lua_pcrefix.h\"\n#include \"ngx_http_lua_script.h\"\n#include \"ngx_http_lua_util.h\"\n\n\n#if (PCRE_MAJOR >= 6 || NGX_PCRE2)\n#   define LUA_HAVE_PCRE_DFA 1\n#else\n#   define LUA_HAVE_PCRE_DFA 0\n#endif\n\n\n#if (NGX_PCRE2)\nstatic pcre2_compile_context  *ngx_regex_compile_context;\nstatic pcre2_match_context    *ngx_regex_match_context;\nstatic pcre2_match_data       *ngx_regex_match_data;\nstatic ngx_uint_t              ngx_regex_match_data_size = 0;\n\n#define PCRE2_VERSION_SIZE     64\nstatic char                    ngx_pcre2_version[PCRE2_VERSION_SIZE];\n#endif\n\n\n#define NGX_LUA_RE_MODE_DFA          (1<<1)\n#define NGX_LUA_RE_MODE_JIT          (1<<2)\n#define NGX_LUA_RE_NO_UTF8_CHECK     (1<<4)\n\n#define NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT (100)\n\n#define NGX_LUA_RE_MIN_JIT_STACK_SIZE 32 * 1024\n\n\ntypedef struct {\n    ngx_pool_t                   *pool;\n    u_char                       *name_table;\n    int                           name_count;\n    int                           name_entry_size;\n\n    int                           ncaptures;\n    int                          *captures;\n\n#if (NGX_PCRE2)\n    pcre2_code                   *regex;\n    /*\n     * pcre2 doesn't use pcre_extra any more,\n     * just for keeping same memory layout in the lua ffi cdef\n     */\n    void                         *regex_sd;\n#else\n    pcre                         *regex;\n    pcre_extra                   *regex_sd;\n#endif\n\n    ngx_http_lua_complex_value_t *replace;\n\n    /* only for (stap) debugging, and may be an invalid pointer */\n    const u_char                 *pattern;\n} ngx_http_lua_regex_t;\n\n\ntypedef struct {\n    ngx_str_t     pattern;\n    ngx_pool_t   *pool;\n    ngx_int_t     options;\n\n#if (NGX_PCRE2)\n    pcre2_code   *regex;\n#else\n    pcre         *regex;\n#endif\n    int           captures;\n    ngx_str_t     err;\n} ngx_http_lua_regex_compile_t;\n\n\ntypedef struct {\n    ngx_http_request_t      *request;\n#if (NGX_PCRE2)\n    pcre2_code              *regex;\n#else\n    pcre                    *regex;\n    pcre_extra              *regex_sd;\n#endif\n    int                      ncaptures;\n    int                     *captures;\n    int                      captures_len;\n    uint8_t                  flags;\n} ngx_http_lua_regex_ctx_t;\n\n\nstatic ngx_int_t ngx_http_lua_regex_compile(ngx_http_lua_regex_compile_t *rc);\n\n\n#define ngx_http_lua_regex_exec(re, e, s, start, captures, size, opts)       \\\n    pcre_exec(re, e, (const char *) (s)->data, (s)->len, start, opts,        \\\n              captures, size)\n\n\n#define ngx_http_lua_regex_dfa_exec(re, e, s, start, captures, size, ws,     \\\n                                    wscount, opts)                           \\\n    pcre_dfa_exec(re, e, (const char *) (s)->data, (s)->len, start, opts,    \\\n                  captures, size, ws, wscount)\n\n\nstatic void\nngx_http_lua_regex_free_study_data(ngx_pool_t *pool, ngx_http_lua_regex_t *re)\n{\n    ngx_pool_t  *old_pool;\n\n#if (NGX_PCRE2)\n    if (re && re->regex) {\n        old_pool = ngx_http_lua_pcre_malloc_init(pool);\n\n        pcre2_code_free(re->regex);\n\n        ngx_http_lua_pcre_malloc_done(old_pool);\n\n        re->regex = NULL;\n    }\n#else\n    if (re && re->regex_sd) {\n        old_pool = ngx_http_lua_pcre_malloc_init(pool);\n#if LUA_HAVE_PCRE_JIT\n        pcre_free_study(re->regex_sd);\n#else\n        pcre_free(re->regex_sd);\n#endif\n        ngx_http_lua_pcre_malloc_done(old_pool);\n\n        re->regex_sd = NULL;\n    }\n#endif\n}\n\n\n#if (NGX_PCRE2)\nstatic ngx_int_t\nngx_http_lua_regex_compile(ngx_http_lua_regex_compile_t *rc)\n{\n    int                     n, errcode;\n    char                   *p;\n    size_t                  erroff;\n    u_char                  errstr[128];\n    pcre2_code             *re;\n    ngx_pool_t             *old_pool;\n    pcre2_general_context  *gctx;\n    pcre2_compile_context  *cctx;\n\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    if (ngx_regex_compile_context == NULL) {\n        /*\n         * Allocate a compile context if not yet allocated.  This uses\n         * direct allocations from heap, so the result can be cached\n         * even at runtime.\n         */\n\n        old_pool = ngx_http_lua_pcre_malloc_init(NULL);\n\n        gctx = pcre2_general_context_create(ngx_http_lua_pcre_malloc,\n                                            ngx_http_lua_pcre_free,\n                                            NULL);\n        if (gctx == NULL) {\n            ngx_http_lua_pcre_malloc_done(old_pool);\n            goto nomem;\n        }\n\n        cctx = pcre2_compile_context_create(gctx);\n        if (cctx == NULL) {\n            pcre2_general_context_free(gctx);\n            ngx_http_lua_pcre_malloc_done(old_pool);\n            goto nomem;\n        }\n\n        ngx_regex_compile_context = cctx;\n\n        ngx_regex_match_context = pcre2_match_context_create(gctx);\n        if (ngx_regex_match_context == NULL) {\n            pcre2_general_context_free(gctx);\n            ngx_http_lua_pcre_malloc_done(old_pool);\n            goto nomem;\n        }\n\n        lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                                   ngx_http_lua_module);\n        if (lmcf && lmcf->regex_match_limit > 0) {\n            pcre2_set_match_limit(ngx_regex_match_context,\n                                  lmcf->regex_match_limit);\n        }\n\n        pcre2_general_context_free(gctx);\n        ngx_http_lua_pcre_malloc_done(old_pool);\n    }\n\n    old_pool = ngx_http_lua_pcre_malloc_init(rc->pool);\n\n    re = pcre2_compile(rc->pattern.data,\n                       rc->pattern.len, rc->options,\n                       &errcode, &erroff, ngx_regex_compile_context);\n\n    ngx_http_lua_pcre_malloc_done(old_pool);\n\n    if (re == NULL) {\n        pcre2_get_error_message(errcode, errstr, 128);\n\n        if ((size_t) erroff == rc->pattern.len) {\n            rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,\n                                       \"pcre2_compile() failed: %s in \\\"%V\\\"\",\n                                       errstr, &rc->pattern)\n                          - rc->err.data;\n\n        } else {\n            rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,\n                                       \"pcre2_compile() failed: %s in \"\n                                       \"\\\"%V\\\" at \\\"%s\\\"\", errstr, &rc->pattern,\n                                       rc->pattern.data + erroff)\n                          - rc->err.data;\n        }\n\n        return NGX_ERROR;\n    }\n\n    rc->regex = re;\n\n    n = pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, &rc->captures);\n    if (n < 0) {\n        p = \"pcre2_pattern_info(\\\"%V\\\", PCRE_INFO_CAPTURECOUNT) failed: %d\";\n        goto failed;\n    }\n\n#if (NGX_DEBUG)\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"pcre2_compile: pattern[%V], options 0x%08Xd, ncaptures %d\",\n                   &rc->pattern, rc->options, rc->captures);\n#endif\n\n    return NGX_OK;\n\nfailed:\n\n    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n)\n                  - rc->err.data;\n    return NGX_ERROR;\n\nnomem:\n\n    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,\n                               \"regex \\\"%V\\\" compilation failed: no memory\",\n                               &rc->pattern)\n                  - rc->err.data;\n    return NGX_ERROR;\n}\n\n#else\n\nstatic ngx_int_t\nngx_http_lua_regex_compile(ngx_http_lua_regex_compile_t *rc)\n{\n    int           n, erroff;\n    char         *p;\n    const char   *errstr;\n    pcre         *re;\n    ngx_pool_t   *old_pool;\n\n    old_pool = ngx_http_lua_pcre_malloc_init(rc->pool);\n\n    re = pcre_compile((const char *) rc->pattern.data, (int) rc->options,\n                      &errstr, &erroff, NULL);\n\n    ngx_http_lua_pcre_malloc_done(old_pool);\n\n    if (re == NULL) {\n        if ((size_t) erroff == rc->pattern.len) {\n            rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,\n                                       \"pcre_compile() failed: %s in \\\"%V\\\"\",\n                                       errstr, &rc->pattern)\n                         - rc->err.data;\n\n        } else {\n            rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,\n                                       \"pcre_compile() failed: %s in \\\"%V\\\" \"\n                                       \"at \\\"%s\\\"\", errstr, &rc->pattern,\n                                       rc->pattern.data + erroff)\n                         - rc->err.data;\n        }\n\n        return NGX_ERROR;\n    }\n\n    rc->regex = re;\n\n#if 1\n    n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures);\n    if (n < 0) {\n        p = \"pcre_fullinfo(\\\"%V\\\", PCRE_INFO_CAPTURECOUNT) failed: %d\";\n        goto failed;\n    }\n#endif\n\n    return NGX_OK;\n\nfailed:\n\n    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n)\n                  - rc->err.data;\n    return NGX_OK;\n}\n#endif\n\n\nngx_int_t\nngx_http_lua_ffi_set_jit_stack_size(int size, u_char *errstr,\n    size_t *errstr_size)\n{\n#if (LUA_HAVE_PCRE_JIT)\n\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_pool_t                  *pool, *old_pool;\n\n    lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                               ngx_http_lua_module);\n\n    ngx_http_lua_assert(lmcf != NULL);\n\n    if (size < NGX_LUA_RE_MIN_JIT_STACK_SIZE) {\n        size = NGX_LUA_RE_MIN_JIT_STACK_SIZE;\n    }\n\n    pool = lmcf->pool;\n\n    dd(\"server pool %p\", lmcf->pool);\n\n    if (lmcf->jit_stack) {\n        old_pool = ngx_http_lua_pcre_malloc_init(pool);\n\n#if (NGX_PCRE2)\n        pcre2_jit_stack_free(lmcf->jit_stack);\n#else\n        pcre_jit_stack_free(lmcf->jit_stack);\n#endif\n\n        ngx_http_lua_pcre_malloc_done(old_pool);\n    }\n\n    old_pool = ngx_http_lua_pcre_malloc_init(pool);\n\n#if (NGX_PCRE2)\n    lmcf->jit_stack = pcre2_jit_stack_create(NGX_LUA_RE_MIN_JIT_STACK_SIZE,\n                                             size, NULL);\n#else\n    lmcf->jit_stack = pcre_jit_stack_alloc(NGX_LUA_RE_MIN_JIT_STACK_SIZE,\n                                           size);\n#endif\n\n    ngx_http_lua_pcre_malloc_done(old_pool);\n\n    if (lmcf->jit_stack == NULL) {\n        *errstr_size = ngx_snprintf(errstr, *errstr_size,\n                                    \"pcre jit stack allocation failed\")\n                       - errstr;\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n\n#else  /* LUA_HAVE_PCRE_JIT */\n\n    *errstr_size = ngx_snprintf(errstr, *errstr_size,\n                                \"no pcre jit support found\")\n                   - errstr;\n    return NGX_ERROR;\n\n#endif\n}\n\n\n#if (NGX_PCRE2)\nstatic void\nngx_http_lua_regex_jit_compile(ngx_http_lua_regex_t *re, int flags,\n    ngx_pool_t *pool, ngx_http_lua_main_conf_t *lmcf,\n    ngx_http_lua_regex_compile_t *re_comp)\n{\n    ngx_int_t    ret;\n    ngx_pool_t  *old_pool;\n\n    if (flags & NGX_LUA_RE_MODE_JIT) {\n        old_pool = ngx_http_lua_pcre_malloc_init(pool);\n        ret = pcre2_jit_compile(re_comp->regex, PCRE2_JIT_COMPLETE);\n\n        if (ret != 0) {\n            ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,\n                          \"pcre2_jit_compile() failed: %d in \\\"%V\\\", \"\n                          \"ignored\",\n                          ret, &re_comp->pattern);\n\n#if (NGX_DEBUG)\n\n        } else {\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                           \"pcre2 JIT compiled successfully\");\n#   endif /* !(NGX_DEBUG) */\n        }\n\n        ngx_http_lua_pcre_malloc_done(old_pool);\n\n    }\n\n    if (lmcf && lmcf->jit_stack) {\n        pcre2_jit_stack_assign(ngx_regex_match_context, NULL,\n                               lmcf->jit_stack);\n    }\n\n    return;\n}\n\n#else\n\nstatic void\nngx_http_lua_regex_jit_compile(ngx_http_lua_regex_t *re, int flags,\n    ngx_pool_t *pool, ngx_http_lua_main_conf_t *lmcf,\n    ngx_http_lua_regex_compile_t *re_comp)\n{\n    const char  *msg;\n    pcre_extra  *sd = NULL;\n    ngx_pool_t  *old_pool;\n\n\n#if (LUA_HAVE_PCRE_JIT)\n    if (flags & NGX_LUA_RE_MODE_JIT) {\n        old_pool = ngx_http_lua_pcre_malloc_init(pool);\n        sd = pcre_study(re_comp->regex, PCRE_STUDY_JIT_COMPILE, &msg);\n        ngx_http_lua_pcre_malloc_done(old_pool);\n\n#   if (NGX_DEBUG)\n        if (msg != NULL) {\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                           \"pcre study failed with PCRE_STUDY_JIT_COMPILE: \"\n                           \"%s (%p)\", msg, sd);\n        }\n\n        if (sd != NULL) {\n            int         jitted;\n\n            old_pool = ngx_http_lua_pcre_malloc_init(pool);\n\n            pcre_fullinfo(re_comp->regex, sd, PCRE_INFO_JIT, &jitted);\n\n            ngx_http_lua_pcre_malloc_done(old_pool);\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                           \"pcre JIT compiling result: %d\", jitted);\n        }\n#   endif /* !(NGX_DEBUG) */\n\n    } else {\n        old_pool = ngx_http_lua_pcre_malloc_init(pool);\n        sd = pcre_study(re_comp->regex, 0, &msg);\n        ngx_http_lua_pcre_malloc_done(old_pool);\n    }\n\n    if (sd && lmcf && lmcf->jit_stack) {\n        pcre_assign_jit_stack(sd, NULL, lmcf->jit_stack);\n    }\n\n    if (sd\n        && lmcf && lmcf->regex_match_limit > 0\n        && !(flags & NGX_LUA_RE_MODE_DFA))\n    {\n        sd->flags |= PCRE_EXTRA_MATCH_LIMIT;\n        sd->match_limit = lmcf->regex_match_limit;\n    }\n\n#endif /* LUA_HAVE_PCRE_JIT */\n\n    re->regex_sd = sd;\n}\n#endif\n\n\n#if (NGX_PCRE2)\nvoid\nngx_http_lua_regex_cleanup(void *data)\n{\n    ngx_pool_t                *old_pool;\n    ngx_http_lua_main_conf_t  *lmcf;\n\n    lmcf = data;\n\n    if (ngx_regex_compile_context) {\n        old_pool = ngx_http_lua_pcre_malloc_init(NULL);\n        if (ngx_regex_match_context != NULL) {\n            pcre2_match_context_free(ngx_regex_match_context);\n            ngx_regex_match_context = NULL;\n        }\n\n        pcre2_compile_context_free(ngx_regex_compile_context);\n        ngx_regex_compile_context = NULL;\n        ngx_http_lua_pcre_malloc_done(old_pool);\n    }\n\n    if (lmcf && lmcf->jit_stack) {\n        old_pool = ngx_http_lua_pcre_malloc_init(NULL);\n\n        pcre2_jit_stack_free(lmcf->jit_stack);\n        lmcf->jit_stack = NULL;\n\n        ngx_http_lua_pcre_malloc_done(old_pool);\n    }\n\n    if (ngx_regex_match_data) {\n        old_pool = ngx_http_lua_pcre_malloc_init(NULL);\n        pcre2_match_data_free(ngx_regex_match_data);\n        ngx_regex_match_data = NULL;\n        ngx_regex_match_data_size = 0;\n        ngx_http_lua_pcre_malloc_done(old_pool);\n    }\n\n}\n#endif\n\n\nngx_http_lua_regex_t *\nngx_http_lua_ffi_compile_regex(const unsigned char *pat, size_t pat_len,\n    int flags, int pcre_opts, u_char *errstr,\n    size_t errstr_size)\n{\n    int                     *cap = NULL, ovecsize;\n    u_char                  *p;\n    ngx_int_t                rc;\n    const char              *msg;\n    ngx_pool_t              *pool, *old_pool;\n    ngx_http_lua_regex_t    *re = NULL;\n\n    ngx_http_lua_main_conf_t         *lmcf;\n    ngx_http_lua_regex_compile_t      re_comp;\n\n    pool = ngx_create_pool(512, ngx_cycle->log);\n    if (pool == NULL) {\n        msg = \"no memory\";\n        goto error;\n    }\n\n    pool->log = (ngx_log_t *) &ngx_cycle->new_log;\n\n    re = ngx_palloc(pool, sizeof(ngx_http_lua_regex_t));\n    if (re == NULL) {\n        ngx_destroy_pool(pool);\n        pool = NULL;\n        msg = \"no memory\";\n        goto error;\n    }\n\n    re->pool = pool;\n    re->regex = NULL;\n    re->regex_sd = NULL;\n\n    re_comp.options      = pcre_opts;\n    re_comp.pattern.data = (u_char *) pat;\n    re_comp.pattern.len  = pat_len;\n    re_comp.err.len      = errstr_size - 1;\n    re_comp.err.data     = errstr;\n    re_comp.pool         = pool;\n\n    old_pool = ngx_http_lua_pcre_malloc_init(pool);\n    rc = ngx_http_lua_regex_compile(&re_comp);\n    ngx_http_lua_pcre_malloc_done(old_pool);\n\n    if (rc != NGX_OK) {\n        re_comp.err.data[re_comp.err.len] = '\\0';\n        msg = (char *) re_comp.err.data;\n        goto error;\n    }\n\n    lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                               ngx_http_lua_module);\n\n    ngx_http_lua_assert(lmcf != NULL);\n\n    ngx_http_lua_regex_jit_compile(re, flags, pool, lmcf, &re_comp);\n\n    if (flags & NGX_LUA_RE_MODE_DFA) {\n        ovecsize = 2;\n        re_comp.captures = 0;\n\n    } else {\n#if (NGX_PCRE2)\n        ovecsize = (re_comp.captures + 1) * 2;\n#else\n        ovecsize = (re_comp.captures + 1) * 3;\n#endif\n    }\n\n    dd(\"allocating cap with size: %d\", (int) ovecsize);\n\n    cap = ngx_palloc(pool, ovecsize * sizeof(int));\n    if (cap == NULL) {\n        msg = \"no memory\";\n        goto error;\n    }\n\n#if (NGX_PCRE2)\n    if (pcre2_pattern_info(re_comp.regex, PCRE2_INFO_NAMECOUNT,\n                           &re->name_count) < 0)\n    {\n        msg = \"cannot acquire named subpattern count\";\n        goto error;\n    }\n\n    if (re->name_count > 0) {\n        if (pcre2_pattern_info(re_comp.regex, PCRE2_INFO_NAMEENTRYSIZE,\n                               &re->name_entry_size) != 0)\n        {\n            msg = \"cannot acquire named subpattern entry size\";\n            goto error;\n        }\n\n        if (pcre2_pattern_info(re_comp.regex, PCRE2_INFO_NAMETABLE,\n                               &re->name_table) != 0)\n        {\n            msg = \"cannot acquire named subpattern table\";\n            goto error;\n        }\n    }\n\n#else\n    if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMECOUNT,\n                      &re->name_count) != 0)\n    {\n        msg = \"cannot acquire named subpattern count\";\n        goto error;\n    }\n\n    if (re->name_count > 0) {\n        if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMEENTRYSIZE,\n                          &re->name_entry_size) != 0)\n        {\n            msg = \"cannot acquire named subpattern entry size\";\n            goto error;\n        }\n\n        if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMETABLE,\n                          &re->name_table) != 0)\n        {\n            msg = \"cannot acquire named subpattern table\";\n            goto error;\n        }\n    }\n#endif\n\n    re->regex = re_comp.regex;\n    re->ncaptures = re_comp.captures;\n    re->captures = cap;\n    re->replace = NULL;\n\n    /* only for (stap) debugging, the pointer might be invalid when the\n     * string is collected later on.... */\n    re->pattern = pat;\n\n    return re;\n\nerror:\n\n    p = ngx_snprintf(errstr, errstr_size - 1, \"%s\", msg);\n    *p = '\\0';\n\n    ngx_http_lua_regex_free_study_data(pool, re);\n\n    if (pool) {\n        ngx_destroy_pool(pool);\n    }\n\n    return NULL;\n}\n\n\n#if (NGX_PCRE2)\nint\nngx_http_lua_ffi_exec_regex(ngx_http_lua_regex_t *re, int flags,\n    const u_char *s, size_t len, int pos)\n{\n    int          rc, exec_opts = 0;\n    size_t      *ov;\n    ngx_uint_t   ovecpair, n, i;\n    ngx_pool_t  *old_pool;\n\n    if (flags & NGX_LUA_RE_MODE_DFA) {\n        ovecpair = 1;\n        re->ncaptures = 0;\n\n    } else {\n        ovecpair = re->ncaptures + 1;\n    }\n\n    old_pool = ngx_http_lua_pcre_malloc_init(NULL);\n\n    if (ngx_regex_match_data == NULL\n        || ovecpair > ngx_regex_match_data_size)\n    {\n        /*\n         * Allocate a match data if not yet allocated or smaller than\n         * needed.\n         */\n\n        if (ngx_regex_match_data) {\n            pcre2_match_data_free(ngx_regex_match_data);\n        }\n\n        ngx_regex_match_data_size = ovecpair;\n        ngx_regex_match_data = pcre2_match_data_create(ovecpair, NULL);\n\n        if (ngx_regex_match_data == NULL) {\n            rc = PCRE2_ERROR_NOMEMORY;\n            goto failed;\n        }\n    }\n\n    if (flags & NGX_LUA_RE_NO_UTF8_CHECK) {\n        exec_opts = PCRE2_NO_UTF_CHECK;\n\n    } else {\n        exec_opts = 0;\n    }\n\n    if (flags & NGX_LUA_RE_MODE_DFA) {\n        int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT];\n        rc = pcre2_dfa_match(re->regex, s, len, pos, exec_opts,\n                             ngx_regex_match_data, ngx_regex_match_context,\n                             ws, sizeof(ws) / sizeof(ws[0]));\n\n\n    } else {\n        rc = pcre2_match(re->regex, s, len, pos, exec_opts,\n                         ngx_regex_match_data, ngx_regex_match_context);\n    }\n\n    if (rc < 0) {\n#if (NGX_DEBUG)\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"pcre2_match failed: flags 0x%05Xd, options 0x%08Xd, \"\n                       \"rc %d, ovecpair %ui\", flags, exec_opts, rc, ovecpair);\n#endif\n\n        goto failed;\n    }\n\n    n = pcre2_get_ovector_count(ngx_regex_match_data);\n    ov = pcre2_get_ovector_pointer(ngx_regex_match_data);\n\n#if (NGX_DEBUG)\n    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"pcre2_match: flags 0x%05Xd, options 0x%08Xd, rc %d, \"\n                   \"n %ui, ovecpair %ui\", flags, exec_opts, rc, n, ovecpair);\n#endif\n\n    if (n > ovecpair) {\n        n = ovecpair;\n    }\n\n    for (i = 0; i < n; i++) {\n        re->captures[i * 2] = ov[i * 2];\n        re->captures[i * 2 + 1] = ov[i * 2 + 1];\n    }\n\nfailed:\n\n    ngx_http_lua_pcre_malloc_done(old_pool);\n\n    return rc;\n}\n\n#else\n\nint\nngx_http_lua_ffi_exec_regex(ngx_http_lua_regex_t *re, int flags,\n    const u_char *s, size_t len, int pos)\n{\n    int             rc, ovecsize, exec_opts, *cap;\n    ngx_str_t       subj;\n    pcre_extra     *sd;\n\n    cap = re->captures;\n    sd = re->regex_sd;\n\n    if (flags & NGX_LUA_RE_MODE_DFA) {\n        ovecsize = 2;\n        re->ncaptures = 0;\n\n    } else {\n        ovecsize = (re->ncaptures + 1) * 3;\n    }\n\n    if (flags & NGX_LUA_RE_NO_UTF8_CHECK) {\n        exec_opts = PCRE_NO_UTF8_CHECK;\n\n    } else {\n        exec_opts = 0;\n    }\n\n    subj.data = (u_char *) s;\n    subj.len = len;\n\n    if (flags & NGX_LUA_RE_MODE_DFA) {\n\n#if LUA_HAVE_PCRE_DFA\n\n        int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT];\n        rc = ngx_http_lua_regex_dfa_exec(re->regex, sd, &subj,\n                                         (int) pos, cap, ovecsize, ws,\n                                         sizeof(ws) / sizeof(ws[0]),\n                                         exec_opts);\n\n#else\n\n        return PCRE_ERROR_BADOPTION;\n\n#endif /* LUA_HAVE_PCRE_DFA */\n\n    } else {\n        rc = ngx_http_lua_regex_exec(re->regex, sd, &subj, (int) pos, cap,\n                                     ovecsize, exec_opts);\n    }\n\n    return rc;\n}\n\n#endif\n\n\nvoid\nngx_http_lua_ffi_destroy_regex(ngx_http_lua_regex_t *re)\n{\n    dd(\"destroy regex called\");\n\n    if (re == NULL || re->pool == NULL) {\n        return;\n    }\n\n    ngx_http_lua_regex_free_study_data(re->pool, re);\n\n    ngx_destroy_pool(re->pool);\n}\n\n\nint\nngx_http_lua_ffi_compile_replace_template(ngx_http_lua_regex_t *re,\n    const u_char *replace_data, size_t replace_len)\n{\n    ngx_int_t                                rc;\n    ngx_str_t                                tpl;\n    ngx_http_lua_complex_value_t            *ctpl;\n    ngx_http_lua_compile_complex_value_t     ccv;\n\n    ctpl = ngx_palloc(re->pool, sizeof(ngx_http_lua_complex_value_t));\n    if (ctpl == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (replace_len != 0) {\n        /* copy the string buffer pointed to by tpl.data from Lua VM */\n        tpl.data = ngx_palloc(re->pool, replace_len + 1);\n        if (tpl.data == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(tpl.data, replace_data, replace_len);\n        tpl.data[replace_len] = '\\0';\n\n    } else {\n        tpl.data = (u_char *) replace_data;\n    }\n\n    tpl.len = replace_len;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_lua_compile_complex_value_t));\n    ccv.pool = re->pool;\n    ccv.log = ngx_cycle->log;\n    ccv.value = &tpl;\n    ccv.complex_value = ctpl;\n\n    rc = ngx_http_lua_compile_complex_value(&ccv);\n\n    re->replace = ctpl;\n\n    return rc;\n}\n\n\nngx_http_lua_script_engine_t *\nngx_http_lua_ffi_create_script_engine(void)\n{\n    return ngx_calloc(sizeof(ngx_http_lua_script_engine_t), ngx_cycle->log);\n}\n\n\nvoid\nngx_http_lua_ffi_init_script_engine(ngx_http_lua_script_engine_t *e,\n    const unsigned char *subj, ngx_http_lua_regex_t *compiled, int count)\n{\n    e->log = ngx_cycle->log;\n    e->ncaptures = count * 2;\n    e->captures = compiled->captures;\n    e->captures_data = (u_char *) subj;\n}\n\n\nvoid\nngx_http_lua_ffi_destroy_script_engine(ngx_http_lua_script_engine_t *e)\n{\n    ngx_free(e);\n}\n\n\nsize_t\nngx_http_lua_ffi_script_eval_len(ngx_http_lua_script_engine_t *e,\n    ngx_http_lua_complex_value_t *val)\n{\n    size_t          len;\n\n    ngx_http_lua_script_len_code_pt   lcode;\n\n    e->ip = val->lengths;\n    len = 0;\n\n    while (*(uintptr_t *) e->ip) {\n        lcode = *(ngx_http_lua_script_len_code_pt *) e->ip;\n        len += lcode(e);\n    }\n\n    return len;\n}\n\n\nvoid\nngx_http_lua_ffi_script_eval_data(ngx_http_lua_script_engine_t *e,\n    ngx_http_lua_complex_value_t *val, u_char *dst)\n{\n    ngx_http_lua_script_code_pt       code;\n\n    e->ip = val->values;\n    e->pos = dst;\n\n    while (*(uintptr_t *) e->ip) {\n        code = *(ngx_http_lua_script_code_pt *) e->ip;\n        code(e);\n    }\n}\n\n\nuint32_t\nngx_http_lua_ffi_max_regex_cache_size(void)\n{\n    ngx_http_lua_main_conf_t    *lmcf;\n    lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                               ngx_http_lua_module);\n    if (lmcf == NULL) {\n        return 0;\n    }\n\n    return (uint32_t) lmcf->regex_cache_max_entries;\n}\n\n\nconst char *\nngx_http_lua_ffi_pcre_version(void)\n{\n#if (NGX_PCRE2)\n    pcre2_config(PCRE2_CONFIG_VERSION, ngx_pcre2_version);\n\n    return ngx_pcre2_version;\n#else\n    return pcre_version();\n#endif\n}\n\n\n#endif /* NGX_PCRE */\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_req_body.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n#include \"ngx_http_lua_req_body.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_headers_in.h\"\n\n\nstatic int ngx_http_lua_ngx_req_read_body(lua_State *L);\nstatic void ngx_http_lua_req_body_post_read(ngx_http_request_t *r);\nstatic int ngx_http_lua_ngx_req_discard_body(lua_State *L);\nstatic int ngx_http_lua_ngx_req_get_body_data(lua_State *L);\nstatic int ngx_http_lua_ngx_req_get_body_file(lua_State *L);\nstatic int ngx_http_lua_ngx_req_set_body_data(lua_State *L);\nstatic void ngx_http_lua_pool_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);\nstatic int ngx_http_lua_ngx_req_set_body_file(lua_State *L);\n\nstatic int ngx_http_lua_ngx_req_init_body(lua_State *L);\nstatic int ngx_http_lua_ngx_req_append_body(lua_State *L);\nstatic int ngx_http_lua_ngx_req_body_finish(lua_State *L);\nstatic ngx_int_t ngx_http_lua_write_request_body(ngx_http_request_t *r,\n    ngx_chain_t *body);\nstatic ngx_int_t ngx_http_lua_read_body_resume(ngx_http_request_t *r);\nstatic void ngx_http_lua_req_body_cleanup(void *data);\n\n\n\nvoid\nngx_http_lua_inject_req_body_api(lua_State *L)\n{\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_read_body);\n    lua_setfield(L, -2, \"read_body\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_discard_body);\n    lua_setfield(L, -2, \"discard_body\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_get_body_data);\n    lua_setfield(L, -2, \"get_body_data\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_get_body_file);\n    lua_setfield(L, -2, \"get_body_file\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_set_body_data);\n    lua_setfield(L, -2, \"set_body_data\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_set_body_file);\n    lua_setfield(L, -2, \"set_body_file\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_init_body);\n    lua_setfield(L, -2, \"init_body\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_append_body);\n    lua_setfield(L, -2, \"append_body\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_body_finish);\n    lua_setfield(L, -2, \"finish_body\");\n}\n\n\nstatic int\nngx_http_lua_ngx_req_read_body(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    int                          n;\n    ngx_int_t                    rc;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    n = lua_gettop(L);\n\n    if (n != 0) {\n        return luaL_error(L, \"expecting 0 arguments but seen %d\", n);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"request object not found\");\n    }\n\n    r->request_body_in_single_buf = 1;\n    r->request_body_in_persistent_file = 1;\n    r->request_body_in_clean_file = 1;\n\n#if 1\n    if (r->request_body_in_file_only) {\n        r->request_body_file_log_level = 0;\n    }\n#endif\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_ACCESS\n                               | NGX_HTTP_LUA_CONTEXT_PRECONTENT\n                               | NGX_HTTP_LUA_CONTEXT_CONTENT);\n\n    coctx = ctx->cur_co_ctx;\n    if (coctx == NULL) {\n        return luaL_error(L, \"no co ctx found\");\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua start to read buffered request body\");\n\n    rc = ngx_http_read_client_request_body(r, ngx_http_lua_req_body_post_read);\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        ctx->exit_code = rc;\n        ctx->exited = 1;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http read client request body returned error code %i, \"\n                       \"exitting now\", rc);\n\n        return lua_yield(L, 0);\n    }\n\n    r->main->count--;\n    dd(\"decrement r->main->count: %d\", (int) r->main->count);\n\n    if (rc == NGX_AGAIN) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua read buffered request body requires I/O \"\n                       \"interruptions\");\n\n        ctx->waiting_more_body = 1;\n        ctx->downstream = coctx;\n\n        ngx_http_lua_cleanup_pending_operation(coctx);\n        coctx->cleanup = ngx_http_lua_req_body_cleanup;\n        coctx->data = r;\n\n        return lua_yield(L, 0);\n    }\n\n    /* rc == NGX_OK */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua has read buffered request body in a single run\");\n\n    return 0;\n}\n\n\nstatic void\nngx_http_lua_req_body_post_read(ngx_http_request_t *r)\n{\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_http_lua_co_ctx_t   *coctx;\n\n    ngx_http_lua_loc_conf_t             *llcf;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua req body post read, c:%ud\", r->main->count);\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (ctx->waiting_more_body) {\n        ctx->waiting_more_body = 0;\n\n        coctx = ctx->downstream;\n        ctx->cur_co_ctx = coctx;\n\n        coctx->cleanup = NULL;\n\n        llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n        if (llcf->check_client_abort) {\n            r->read_event_handler = ngx_http_lua_rd_check_broken_connection;\n\n        } else {\n            r->read_event_handler = ngx_http_block_reading;\n        }\n\n        if (ctx->entered_content_phase) {\n            (void) ngx_http_lua_read_body_resume(r);\n\n        } else {\n            ctx->resume_handler = ngx_http_lua_read_body_resume;\n            ngx_http_core_run_phases(r);\n        }\n    }\n}\n\n\nstatic int\nngx_http_lua_ngx_req_discard_body(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    ngx_int_t                    rc;\n    int                          n;\n\n    n = lua_gettop(L);\n\n    if (n != 0) {\n        return luaL_error(L, \"expecting 0 arguments but seen %d\", n);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"request object not found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        return luaL_error(L, \"failed to discard request body\");\n    }\n\n    return 0;\n}\n\n\nstatic int\nngx_http_lua_ngx_req_get_body_data(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    int                          n;\n    size_t                       len, max;\n    size_t                       size, rest;\n    ngx_chain_t                 *cl;\n    u_char                      *p;\n    u_char                      *buf;\n\n    n = lua_gettop(L);\n\n    if (n != 0 && n != 1) {\n        return luaL_error(L, \"expecting 0 or 1 arguments but seen %d\", n);\n    }\n\n    max = 0;\n    if (n == 1) {\n        max = (size_t) luaL_checknumber(L, 1);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"request object not found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    if (r->request_body == NULL\n        || r->request_body->temp_file\n        || r->request_body->bufs == NULL)\n    {\n        lua_pushnil(L);\n        return 1;\n    }\n\n    cl = r->request_body->bufs;\n\n    if (cl->next == NULL) {\n        len = cl->buf->last - cl->buf->pos;\n\n        if (len == 0) {\n            lua_pushnil(L);\n            return 1;\n        }\n\n        len = (max > 0 && len > max) ? max : len;\n        lua_pushlstring(L, (char *) cl->buf->pos, len);\n        return 1;\n    }\n\n    /* found multi-buffer body */\n\n    len = 0;\n\n    for (; cl; cl = cl->next) {\n        dd(\"body chunk len: %d\", (int) ngx_buf_size(cl->buf));\n        size = cl->buf->last - cl->buf->pos;\n        if (max > 0 && (len + size > max)) {\n            len = max;\n            break;\n        }\n\n        len += size;\n    }\n\n    if (len == 0) {\n        lua_pushnil(L);\n        return 1;\n    }\n\n    buf = (u_char *) lua_newuserdata(L, len);\n\n    p = buf;\n    rest = len;\n    for (cl = r->request_body->bufs; cl != NULL && rest > 0; cl = cl->next) {\n        size = ngx_buf_size(cl->buf);\n        if (size > rest) { /* reach limit*/\n            size = rest;\n        }\n\n        p = ngx_copy(p, cl->buf->pos, size);\n        rest -= size;\n    }\n\n    lua_pushlstring(L, (char *) buf, len);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_ngx_req_get_body_file(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    int                          n;\n\n    n = lua_gettop(L);\n\n    if (n != 0) {\n        return luaL_error(L, \"expecting 0 arguments but seen %d\", n);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"request object not found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    if (r->request_body == NULL || r->request_body->temp_file == NULL) {\n        lua_pushnil(L);\n        return 1;\n    }\n\n    dd(\"XXX file directio: %u, f:%u, m:%u, t:%u, end - pos %d, size %d\",\n       r->request_body->temp_file->file.directio,\n       r->request_body->bufs->buf->in_file,\n       r->request_body->bufs->buf->memory,\n       r->request_body->bufs->buf->temporary,\n       (int) (r->request_body->bufs->buf->end -\n              r->request_body->bufs->buf->pos),\n       (int) ngx_buf_size(r->request_body->bufs->buf));\n\n    lua_pushlstring(L, (char *) r->request_body->temp_file->file.name.data,\n                    r->request_body->temp_file->file.name.len);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_ngx_req_set_body_data(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    int                          n;\n    ngx_http_request_body_t     *rb;\n    ngx_temp_file_t             *tf;\n    ngx_buf_t                   *b;\n    ngx_str_t                    body, key, value;\n#if 1\n    ngx_int_t                    rc;\n#endif\n    ngx_chain_t                 *cl;\n    ngx_buf_tag_t                tag;\n\n    n = lua_gettop(L);\n\n    if (n != 1) {\n        return luaL_error(L, \"expecting 1 arguments but seen %d\", n);\n    }\n\n    body.data = (u_char *) luaL_checklstring(L, 1, &body.len);\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"request object not found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    if (r->discard_body) {\n        return luaL_error(L, \"request body already discarded asynchronously\");\n    }\n\n    if (r->request_body == NULL) {\n        return luaL_error(L, \"request body not read yet\");\n    }\n\n    rb = r->request_body;\n\n    tag = (ngx_buf_tag_t) &ngx_http_lua_module;\n\n    tf = rb->temp_file;\n\n    if (tf) {\n        if (tf->file.fd != NGX_INVALID_FILE) {\n\n            dd(\"cleaning temp file %.*s\", (int) tf->file.name.len,\n               tf->file.name.data);\n\n            ngx_http_lua_pool_cleanup_file(r->pool, tf->file.fd);\n            tf->file.fd = NGX_INVALID_FILE;\n\n            dd(\"temp file cleaned: %.*s\", (int) tf->file.name.len,\n               tf->file.name.data);\n        }\n\n        rb->temp_file = NULL;\n    }\n\n    if (body.len == 0) {\n\n        if (rb->bufs) {\n\n            for (cl = rb->bufs; cl; cl = cl->next) {\n                if (cl->buf->tag == tag && cl->buf->temporary) {\n\n                    dd(\"free old request body buffer: size:%d\",\n                       (int) ngx_buf_size(cl->buf));\n\n                    ngx_pfree(r->pool, cl->buf->start);\n                    cl->buf->tag = (ngx_buf_tag_t) NULL;\n                    cl->buf->temporary = 0;\n                }\n            }\n        }\n\n        rb->bufs = NULL;\n        rb->buf = NULL;\n\n        dd(\"request body is set to empty string\");\n        goto set_header;\n    }\n\n    if (rb->bufs) {\n\n        for (cl = rb->bufs; cl; cl = cl->next) {\n            if (cl->buf->tag == tag && cl->buf->temporary) {\n                dd(\"free old request body buffer: size:%d\",\n                   (int) ngx_buf_size(cl->buf));\n\n                ngx_pfree(r->pool, cl->buf->start);\n                cl->buf->tag = (ngx_buf_tag_t) NULL;\n                cl->buf->temporary = 0;\n            }\n        }\n\n        rb->bufs->next = NULL;\n\n        b = rb->bufs->buf;\n\n        ngx_memzero(b, sizeof(ngx_buf_t));\n\n        b->temporary = 1;\n        b->tag = tag;\n\n        b->start = ngx_palloc(r->pool, body.len);\n        if (b->start == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        b->end = b->start + body.len;\n\n        b->pos = b->start;\n        b->last = ngx_copy(b->pos, body.data, body.len);\n\n    } else {\n\n        rb->bufs = ngx_alloc_chain_link(r->pool);\n        if (rb->bufs == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        rb->bufs->next = NULL;\n\n        b = ngx_create_temp_buf(r->pool, body.len);\n        if (b == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        b->tag = tag;\n        b->last = ngx_copy(b->pos, body.data, body.len);\n\n        rb->bufs->buf = b;\n        rb->buf = b;\n    }\n\nset_header:\n\n    /* override input header Content-Length (value must be null terminated) */\n\n    value.data = ngx_palloc(r->pool, NGX_SIZE_T_LEN + 1);\n    if (value.data == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    value.len = ngx_sprintf(value.data, \"%uz\", body.len) - value.data;\n    value.data[value.len] = '\\0';\n\n    dd(\"setting request Content-Length to %.*s (%d)\",\n       (int) value.len, value.data, (int) body.len);\n\n    r->headers_in.content_length_n = body.len;\n\n    if (r->headers_in.content_length) {\n        r->headers_in.content_length->value.data = value.data;\n        r->headers_in.content_length->value.len = value.len;\n\n    } else {\n\n        ngx_str_set(&key, \"Content-Length\");\n\n        rc = ngx_http_lua_set_input_header(r, key, value, 1 /* override */);\n        if (rc != NGX_OK) {\n            return luaL_error(L, \"failed to reset the Content-Length \"\n                              \"input header\");\n        }\n    }\n\n    return 0;\n}\n\n\nstatic int\nngx_http_lua_ngx_req_init_body(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    int                          n;\n    ngx_http_request_body_t     *rb;\n    size_t                       size;\n    lua_Integer                  num;\n#if 1\n    ngx_temp_file_t             *tf;\n#endif\n    ngx_http_core_loc_conf_t    *clcf;\n\n    n = lua_gettop(L);\n\n    if (n != 1 && n != 0) {\n        return luaL_error(L, \"expecting 0 or 1 argument but seen %d\", n);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    if (r->discard_body) {\n        return luaL_error(L, \"request body already discarded asynchronously\");\n    }\n\n    if (r->request_body == NULL) {\n        return luaL_error(L, \"request body not read yet\");\n    }\n\n    if (n == 1) {\n        num = luaL_checkinteger(L, 1);\n        if (num < 0) {\n            return luaL_error(L, \"bad size argument: %d\", (int) num);\n        }\n\n        size = (size_t) num;\n\n    } else {\n\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n        size = clcf->client_body_buffer_size;\n    }\n\n    if (size == 0) {\n        r->request_body_in_file_only = 1;\n    }\n\n    rb = r->request_body;\n\n#if 1\n    tf = rb->temp_file;\n\n    if (tf) {\n        if (tf->file.fd != NGX_INVALID_FILE) {\n\n            dd(\"cleaning temp file %.*s\", (int) tf->file.name.len,\n               tf->file.name.data);\n\n            ngx_http_lua_pool_cleanup_file(r->pool, tf->file.fd);\n\n            ngx_memzero(tf, sizeof(ngx_temp_file_t));\n\n            tf->file.fd = NGX_INVALID_FILE;\n\n            dd(\"temp file cleaned: %.*s\", (int) tf->file.name.len,\n               tf->file.name.data);\n        }\n\n        rb->temp_file = NULL;\n    }\n#endif\n\n    r->request_body_in_clean_file = 1;\n\n    r->headers_in.content_length_n = 0;\n\n    rb->buf = ngx_create_temp_buf(r->pool, size);\n    if (rb->buf == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    rb->bufs = ngx_alloc_chain_link(r->pool);\n    if (rb->bufs == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    rb->bufs->buf = rb->buf;\n    rb->bufs->next = NULL;\n\n    return 0;\n}\n\n\nstatic int\nngx_http_lua_ngx_req_append_body(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    int                          n;\n    ngx_http_request_body_t     *rb;\n    ngx_str_t                    body;\n    size_t                       size, rest;\n    size_t                       offset = 0;\n    ngx_chain_t                  chain;\n    ngx_buf_t                    buf;\n\n    n = lua_gettop(L);\n\n    if (n != 1) {\n        return luaL_error(L, \"expecting 1 arguments but seen %d\", n);\n    }\n\n    body.data = (u_char *) luaL_checklstring(L, 1, &body.len);\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    if (r->request_body == NULL\n        || r->request_body->buf == NULL\n        || r->request_body->bufs == NULL)\n    {\n        return luaL_error(L, \"request_body not initialized\");\n    }\n\n    if (r->request_body_in_file_only) {\n        buf.start = body.data;\n        buf.pos = buf.start;\n        buf.last = buf.start + body.len;\n        buf.end = buf.last;\n        buf.temporary = 1;\n\n        chain.buf = &buf;\n        chain.next = NULL;\n\n        if (ngx_http_lua_write_request_body(r, &chain) != NGX_OK) {\n            return luaL_error(L, \"fail to write file\");\n        }\n\n        r->headers_in.content_length_n += body.len;\n        return 0;\n    }\n\n    rb = r->request_body;\n\n    rest = body.len;\n\n    while (rest > 0) {\n        if (rb->buf->last == rb->buf->end) {\n            if (ngx_http_lua_write_request_body(r, rb->bufs) != NGX_OK) {\n                return luaL_error(L, \"fail to write file\");\n            }\n\n            rb->buf->last = rb->buf->start;\n        }\n\n        size = rb->buf->end - rb->buf->last;\n\n        if (size > rest) {\n            size = rest;\n        }\n\n        ngx_memcpy(rb->buf->last, body.data + offset, size);\n\n        rb->buf->last += size;\n        rest -= size;\n        offset += size;\n        r->headers_in.content_length_n += size;\n    }\n\n    return 0;\n}\n\n\nstatic int\nngx_http_lua_ngx_req_body_finish(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    int                          n;\n    ngx_http_request_body_t     *rb;\n    ngx_buf_t                   *b;\n    size_t                       size;\n    ngx_str_t                    value;\n    ngx_str_t                    key;\n    ngx_int_t                    rc;\n\n    n = lua_gettop(L);\n\n    if (n != 0) {\n        return luaL_error(L, \"expecting 0 argument but seen %d\", n);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    if (r->request_body == NULL\n        || r->request_body->buf == NULL\n        || r->request_body->bufs == NULL)\n    {\n        return luaL_error(L, \"request_body not initialized\");\n    }\n\n    rb = r->request_body;\n\n    if (rb->temp_file) {\n\n        /* save the last part */\n\n        if (ngx_http_lua_write_request_body(r, rb->bufs) != NGX_OK) {\n            return luaL_error(L, \"fail to write file\");\n        }\n\n        b = ngx_calloc_buf(r->pool);\n        if (b == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        b->in_file = 1;\n        b->file_pos = 0;\n        b->file_last = rb->temp_file->file.offset;\n        b->file = &rb->temp_file->file;\n\n        if (rb->bufs->next) {\n            rb->bufs->next->buf = b;\n\n        } else {\n            rb->bufs->buf = b;\n        }\n    }\n\n    /* override input header Content-Length (value must be null terminated) */\n\n    value.data = ngx_palloc(r->pool, NGX_SIZE_T_LEN + 1);\n    if (value.data == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    size = (size_t) r->headers_in.content_length_n;\n\n    value.len = ngx_sprintf(value.data, \"%uz\", size) - value.data;\n    value.data[value.len] = '\\0';\n\n    dd(\"setting request Content-Length to %.*s (%d)\", (int) value.len,\n       value.data, (int) size);\n\n    if (r->headers_in.content_length) {\n        r->headers_in.content_length->value.data = value.data;\n        r->headers_in.content_length->value.len = value.len;\n\n    } else {\n\n        ngx_str_set(&key, \"Content-Length\");\n\n        rc = ngx_http_lua_set_input_header(r, key, value, 1 /* override */);\n        if (rc != NGX_OK) {\n            return luaL_error(L, \"failed to reset the Content-Length \"\n                              \"input header\");\n        }\n    }\n\n    return 0;\n\n}\n\n\nstatic void\nngx_http_lua_pool_cleanup_file(ngx_pool_t *p, ngx_fd_t fd)\n{\n    ngx_pool_cleanup_t       *c;\n    ngx_pool_cleanup_file_t  *cf;\n\n    for (c = p->cleanup; c; c = c->next) {\n        if (c->handler == ngx_pool_cleanup_file\n            || c->handler == ngx_pool_delete_file)\n        {\n            cf = c->data;\n\n            if (cf->fd == fd) {\n                c->handler(cf);\n                c->handler = NULL;\n                return;\n            }\n        }\n    }\n}\n\n\nstatic int\nngx_http_lua_ngx_req_set_body_file(lua_State *L)\n{\n    u_char                      *p;\n    ngx_http_request_t          *r;\n    int                          n;\n    ngx_http_request_body_t     *rb;\n    ngx_temp_file_t             *tf;\n    ngx_buf_t                   *b;\n    ngx_str_t                    name;\n    ngx_int_t                    rc;\n    int                          clean;\n    ngx_open_file_info_t         of;\n    ngx_str_t                    key, value;\n    ngx_pool_cleanup_t          *cln;\n    ngx_pool_cleanup_file_t     *clnf;\n    ngx_err_t                    err;\n    ngx_chain_t                 *cl;\n    ngx_buf_tag_t                tag;\n\n    n = lua_gettop(L);\n\n    if (n != 1 && n != 2) {\n        return luaL_error(L, \"expecting 1 or 2 arguments but seen %d\", n);\n    }\n\n    p = (u_char *) luaL_checklstring(L, 1, &name.len);\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    if (r->discard_body) {\n        return luaL_error(L, \"request body already discarded asynchronously\");\n    }\n\n    if (r->request_body == NULL) {\n        return luaL_error(L, \"request body not read yet\");\n    }\n\n    name.data = ngx_palloc(r->pool, name.len + 1);\n    if (name.data == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    ngx_memcpy(name.data, p, name.len);\n    name.data[name.len] = '\\0';\n\n    if (n == 2) {\n        luaL_checktype(L, 2, LUA_TBOOLEAN);\n        clean = lua_toboolean(L, 2);\n\n    } else {\n        clean = 0;\n    }\n\n    dd(\"clean: %d\", (int) clean);\n\n    rb = r->request_body;\n\n    /* clean up existing r->request_body->bufs (if any) */\n\n    tag = (ngx_buf_tag_t) &ngx_http_lua_module;\n\n    if (rb->bufs) {\n        dd(\"XXX reusing buf\");\n\n        for (cl = rb->bufs; cl; cl = cl->next) {\n            if (cl->buf->tag == tag && cl->buf->temporary) {\n                dd(\"free old request body buffer: size:%d\",\n                   (int) ngx_buf_size(cl->buf));\n\n                ngx_pfree(r->pool, cl->buf->start);\n                cl->buf->tag = (ngx_buf_tag_t) NULL;\n                cl->buf->temporary = 0;\n            }\n        }\n\n        rb->bufs->next = NULL;\n        b = rb->bufs->buf;\n\n        ngx_memzero(b, sizeof(ngx_buf_t));\n\n        b->tag = tag;\n        rb->buf = NULL;\n\n    } else {\n\n        dd(\"XXX creating new buf\");\n\n        rb->bufs = ngx_alloc_chain_link(r->pool);\n        if (rb->bufs == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        rb->bufs->next = NULL;\n\n        b = ngx_calloc_buf(r->pool);\n        if (b == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        b->tag = tag;\n\n        rb->bufs->buf = b;\n        rb->buf = NULL;\n    }\n\n    b->last_in_chain = 1;\n\n    /* just make r->request_body->temp_file a bare stub */\n\n    tf = rb->temp_file;\n\n    if (tf) {\n        if (tf->file.fd != NGX_INVALID_FILE) {\n\n            dd(\"cleaning temp file %.*s\", (int) tf->file.name.len,\n               tf->file.name.data);\n\n            ngx_http_lua_pool_cleanup_file(r->pool, tf->file.fd);\n\n            ngx_memzero(tf, sizeof(ngx_temp_file_t));\n\n            tf->file.fd = NGX_INVALID_FILE;\n\n            dd(\"temp file cleaned: %.*s\", (int) tf->file.name.len,\n               tf->file.name.data);\n        }\n\n    } else {\n\n        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));\n        if (tf == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        tf->file.fd = NGX_INVALID_FILE;\n        rb->temp_file = tf;\n    }\n\n    /* read the file info and construct an in-file buf */\n\n    ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n    of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;\n\n    if (ngx_http_lua_open_and_stat_file(name.data, &of, r->connection->log)\n        != NGX_OK)\n    {\n        return luaL_error(L, \"%s \\\"%s\\\" failed\", of.failed, name.data);\n    }\n\n    dd(\"XXX new body file fd: %d\", of.fd);\n\n    tf->file.fd = of.fd;\n    tf->file.name = name;\n    tf->file.log = r->connection->log;\n    tf->file.directio = 0;\n\n    if (of.size == 0) {\n        if (clean) {\n            if (ngx_delete_file(name.data) == NGX_FILE_ERROR) {\n                err = ngx_errno;\n\n                if (err != NGX_ENOENT) {\n                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,\n                                  ngx_delete_file_n \" \\\"%s\\\" failed\",\n                                  name.data);\n                }\n            }\n        }\n\n        if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,\n                          ngx_close_file_n \" \\\"%s\\\" failed\", name.data);\n        }\n\n        r->request_body->bufs = NULL;\n        r->request_body->buf = NULL;\n\n        goto set_header;\n    }\n\n    /* register file cleanup hook */\n\n    cln = ngx_pool_cleanup_add(r->pool,\n                               sizeof(ngx_pool_cleanup_file_t));\n\n    if (cln == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    cln->handler = clean ? ngx_pool_delete_file : ngx_pool_cleanup_file;\n    clnf = cln->data;\n\n    clnf->fd = of.fd;\n    clnf->name = name.data;\n    clnf->log = r->pool->log;\n\n    b->file = &tf->file;\n    if (b->file == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    dd(\"XXX file size: %d\", (int) of.size);\n\n    b->file_pos = 0;\n    b->file_last = of.size;\n\n    b->in_file = 1;\n\n    dd(\"buf file: %p, f:%u\", b->file, b->in_file);\n\nset_header:\n\n    /* override input header Content-Length (value must be null terminated) */\n\n    value.data = ngx_palloc(r->pool, NGX_OFF_T_LEN + 1);\n    if (value.data == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    value.len = ngx_sprintf(value.data, \"%O\", of.size) - value.data;\n    value.data[value.len] = '\\0';\n\n    r->headers_in.content_length_n = of.size;\n\n    if (r->headers_in.content_length) {\n        r->headers_in.content_length->value.data = value.data;\n        r->headers_in.content_length->value.len = value.len;\n\n    } else {\n\n        ngx_str_set(&key, \"Content-Length\");\n\n        rc = ngx_http_lua_set_input_header(r, key, value, 1 /* override */);\n        if (rc != NGX_OK) {\n            return luaL_error(L, \"failed to reset the Content-Length \"\n                              \"input header\");\n        }\n    }\n\n    return 0;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_write_request_body(ngx_http_request_t *r, ngx_chain_t *body)\n{\n    ssize_t                    n;\n    ngx_temp_file_t           *tf;\n    ngx_http_request_body_t   *rb;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    rb = r->request_body;\n\n    if (rb->temp_file == NULL) {\n        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));\n        if (tf == NULL) {\n            return NGX_ERROR;\n        }\n\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n        tf->file.fd = NGX_INVALID_FILE;\n        tf->file.log = r->connection->log;\n        tf->path = clcf->client_body_temp_path;\n        tf->pool = r->pool;\n        tf->warn = \"a client request body is buffered to a temporary file\";\n        tf->log_level = r->request_body_file_log_level;\n        tf->persistent = 1;\n        tf->clean = 1;\n\n        if (r->request_body_file_group_access) {\n            tf->access = 0660;\n        }\n\n        rb->temp_file = tf;\n\n        if (body == NULL) {\n            /* empty body with r->request_body_in_file_only */\n\n            if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,\n                                     tf->persistent, tf->clean, tf->access)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            return NGX_OK;\n        }\n    }\n\n    n = ngx_write_chain_to_temp_file(rb->temp_file, body);\n\n    /* TODO: n == 0 or not complete and level event */\n\n    if (n == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    rb->temp_file->offset += n;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_read_body_resume(ngx_http_request_t *r)\n{\n    lua_State                   *vm;\n    ngx_int_t                    rc;\n    ngx_uint_t                   nreqs;\n    ngx_connection_t            *c;\n    ngx_http_lua_ctx_t          *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    ctx->resume_handler = ngx_http_lua_wev_handler;\n\n    c = r->connection;\n    vm = ngx_http_lua_get_lua_vm(r, ctx);\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(vm, r, ctx, 0);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run thread returned %d\", rc);\n\n    if (rc == NGX_AGAIN) {\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (ctx->entered_content_phase) {\n        ngx_http_lua_finalize_request(r, rc);\n        return NGX_DONE;\n    }\n\n    return rc;\n}\n\n\nstatic void\nngx_http_lua_req_body_cleanup(void *data)\n{\n    ngx_http_request_t                  *r;\n    ngx_http_lua_ctx_t                  *ctx;\n    ngx_http_lua_co_ctx_t               *coctx = data;\n\n    r = coctx->data;\n    if (r == NULL) {\n        return;\n    }\n\n    if (r->connection->read->timer_set) {\n        ngx_del_timer(r->connection->read);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return;\n    }\n\n    ctx->waiting_more_body = 0;\n    r->keepalive = 0;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_req_body.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_REQ_BODY_H_INCLUDED_\n#define _NGX_HTTP_LUA_REQ_BODY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_req_body_api(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_REQ_BODY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_req_method.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n\n\n#include \"ddebug.h\"\n#include \"ngx_http_lua_subrequest.h\"\n\n\nint\nngx_http_lua_ffi_req_get_method(ngx_http_request_t *r)\n{\n    if (r->connection->fd == (ngx_socket_t) -1) {\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    return r->method;\n}\n\n\nint\nngx_http_lua_ffi_req_get_method_name(ngx_http_request_t *r, u_char **name,\n    size_t *len)\n{\n    if (r->connection->fd == (ngx_socket_t) -1) {\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    *name = r->method_name.data;\n    *len = r->method_name.len;\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_req_set_method(ngx_http_request_t *r, int method)\n{\n    if (r->connection->fd == (ngx_socket_t) -1) {\n        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;\n    }\n\n    switch (method) {\n        case NGX_HTTP_GET:\n            r->method_name = ngx_http_lua_get_method;\n            break;\n\n        case NGX_HTTP_POST:\n            r->method_name = ngx_http_lua_post_method;\n            break;\n\n        case NGX_HTTP_PUT:\n            r->method_name = ngx_http_lua_put_method;\n            break;\n\n        case NGX_HTTP_HEAD:\n            r->method_name = ngx_http_lua_head_method;\n            break;\n\n        case NGX_HTTP_DELETE:\n            r->method_name = ngx_http_lua_delete_method;\n            break;\n\n        case NGX_HTTP_OPTIONS:\n            r->method_name = ngx_http_lua_options_method;\n            break;\n\n        case NGX_HTTP_MKCOL:\n            r->method_name = ngx_http_lua_mkcol_method;\n            break;\n\n        case NGX_HTTP_COPY:\n            r->method_name = ngx_http_lua_copy_method;\n            break;\n\n        case NGX_HTTP_MOVE:\n            r->method_name = ngx_http_lua_move_method;\n            break;\n\n        case NGX_HTTP_PROPFIND:\n            r->method_name = ngx_http_lua_propfind_method;\n            break;\n\n        case NGX_HTTP_PROPPATCH:\n            r->method_name = ngx_http_lua_proppatch_method;\n            break;\n\n        case NGX_HTTP_LOCK:\n            r->method_name = ngx_http_lua_lock_method;\n            break;\n\n        case NGX_HTTP_UNLOCK:\n            r->method_name = ngx_http_lua_unlock_method;\n            break;\n\n        case NGX_HTTP_PATCH:\n            r->method_name = ngx_http_lua_patch_method;\n            break;\n\n        case NGX_HTTP_TRACE:\n            r->method_name = ngx_http_lua_trace_method;\n            break;\n\n        default:\n            return NGX_DECLINED;\n    }\n\n    r->method = method;\n    return NGX_OK;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_rewriteby.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n#include <nginx.h>\n#include \"ngx_http_lua_rewriteby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_exception.h\"\n#include \"ngx_http_lua_cache.h\"\n\n\nstatic ngx_int_t ngx_http_lua_rewrite_by_chunk(lua_State *L,\n    ngx_http_request_t *r);\n\n\nngx_int_t\nngx_http_lua_rewrite_handler(ngx_http_request_t *r)\n{\n    ngx_http_lua_loc_conf_t     *llcf;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_int_t                    rc;\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    /* XXX we need to take into account ngx_rewrite's location dump */\n    if (r->uri_changed) {\n        return NGX_DECLINED;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua rewrite handler, uri:\\\"%V\\\" c:%ud\", &r->uri,\n                   r->main->count);\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    if (!lmcf->postponed_to_rewrite_phase_end) {\n        ngx_http_core_main_conf_t       *cmcf;\n        ngx_http_phase_handler_t        tmp;\n        ngx_http_phase_handler_t        *ph;\n        ngx_http_phase_handler_t        *cur_ph;\n        ngx_http_phase_handler_t        *last_ph;\n\n        lmcf->postponed_to_rewrite_phase_end = 1;\n\n        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n        ph = cmcf->phase_engine.handlers;\n        cur_ph = &ph[r->phase_handler];\n        last_ph = &ph[cur_ph->next - 1];\n\n#if 0\n        if (cur_ph == last_ph) {\n            dd(\"XXX our handler is already the last rewrite phase handler\");\n        }\n#endif\n\n        if (cur_ph < last_ph) {\n            dd(\"swapping the contents of cur_ph and last_ph...\");\n\n            tmp      = *cur_ph;\n\n            memmove(cur_ph, cur_ph + 1,\n                    (last_ph - cur_ph) * sizeof (ngx_http_phase_handler_t));\n\n            *last_ph = tmp;\n\n            r->phase_handler--; /* redo the current ph */\n\n            return NGX_DECLINED;\n        }\n    }\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->rewrite_handler == NULL) {\n        dd(\"no rewrite handler found\");\n        return NGX_DECLINED;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    dd(\"ctx = %p\", ctx);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    dd(\"entered? %d\", (int) ctx->entered_rewrite_phase);\n\n    if (ctx->entered_rewrite_phase) {\n        dd(\"rewriteby: calling wev handler\");\n        rc = ctx->resume_handler(r);\n        dd(\"rewriteby: wev handler returns %d\", (int) rc);\n\n        if (rc == NGX_OK) {\n            rc = NGX_DECLINED;\n        }\n\n        if (rc == NGX_DECLINED) {\n            if (r->header_sent\n                || (r->headers_out.status != 0 && ctx->out != NULL))\n            {\n                dd(\"header already sent\");\n\n                /* response header was already generated in rewrite_by_lua*,\n                 * so it is no longer safe to proceed to later phases\n                 * which may generate responses again */\n\n                if (!ctx->eof) {\n                    dd(\"eof not yet sent\");\n\n                    rc = ngx_http_lua_send_chain_link(r, ctx, NULL\n                                                     /* indicate last_buf */);\n                    if (rc == NGX_ERROR || rc > NGX_OK) {\n                        return rc;\n                    }\n                }\n\n                return NGX_HTTP_OK;\n            }\n\n            r->write_event_handler = ngx_http_core_run_phases;\n            ctx->entered_rewrite_phase = 0;\n\n            return NGX_DECLINED;\n        }\n\n        return rc;\n    }\n\n    if (ctx->waiting_more_body) {\n        return NGX_DONE;\n    }\n\n    if (llcf->force_read_body && !ctx->read_body_done) {\n        r->request_body_in_single_buf = 1;\n        r->request_body_in_persistent_file = 1;\n        r->request_body_in_clean_file = 1;\n\n        rc = ngx_http_read_client_request_body(r,\n                                       ngx_http_lua_generic_phase_post_read);\n\n        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n\n        if (rc == NGX_AGAIN) {\n            ctx->waiting_more_body = 1;\n            return NGX_DONE;\n        }\n    }\n\n    dd(\"calling rewrite handler\");\n    return llcf->rewrite_handler(r);\n}\n\n\nngx_int_t\nngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r)\n{\n    lua_State                   *L;\n    ngx_int_t                    rc;\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    dd(\"rewrite by lua inline\");\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->rewrite_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua inline script (w/ cache) sp = 1 */\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       llcf->rewrite_src.value.data,\n                                       llcf->rewrite_src.value.len,\n                                       &llcf->rewrite_src_ref,\n                                       llcf->rewrite_src_key,\n                                       (const char *)\n                                       llcf->rewrite_chunkname);\n    if (rc != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    return ngx_http_lua_rewrite_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_rewrite_handler_file(ngx_http_request_t *r)\n{\n    lua_State                       *L;\n    ngx_int_t                        rc;\n    u_char                          *script_path;\n    ngx_http_lua_loc_conf_t         *llcf;\n    ngx_str_t                        eval_src;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (ngx_http_complex_value(r, &llcf->rewrite_src, &eval_src) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,\n                                           eval_src.len);\n\n    if (script_path == NULL) {\n        return NGX_ERROR;\n    }\n\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (!llcf->enable_code_cache) {\n        llcf->rewrite_src_ref = LUA_REFNIL;\n    }\n\n    /*  load Lua script file (w/ cache)        sp = 1 */\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,\n                                     &llcf->rewrite_src_ref,\n                                     llcf->rewrite_src_key);\n    if (rc != NGX_OK) {\n        if (rc < NGX_HTTP_SPECIAL_RESPONSE) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        return rc;\n    }\n\n    return ngx_http_lua_rewrite_by_chunk(L, r);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    int                      co_ref;\n    lua_State               *co;\n    ngx_int_t                rc;\n    ngx_uint_t               nreqs;\n    ngx_event_t             *rev;\n    ngx_connection_t        *c;\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_pool_cleanup_t      *cln;\n\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    /*  {{{ new coroutine to handle request */\n    co = ngx_http_lua_new_thread(r, L, &co_ref);\n\n    if (co == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"lua: failed to create new coroutine to handle request\");\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    /*  move code closure to new coroutine */\n    lua_xmove(L, co, 1);\n\n#ifndef OPENRESTY_LUAJIT\n    /*  set closure's env table to new coroutine's globals table */\n    ngx_http_lua_get_globals_table(co);\n    lua_setfenv(co, -2);\n#endif\n\n    /*  save nginx request in coroutine globals table */\n    ngx_http_lua_set_req(co, r);\n\n    /*  {{{ initialize request context */\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    dd(\"ctx = %p\", ctx);\n\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_lua_reset_ctx(r, L, ctx);\n\n    ctx->entered_rewrite_phase = 1;\n\n    ctx->cur_co_ctx = &ctx->entry_co_ctx;\n    ctx->cur_co_ctx->co = co;\n    ctx->cur_co_ctx->co_ref = co_ref;\n#ifdef NGX_LUA_USE_ASSERT\n    ctx->cur_co_ctx->co_top = 1;\n#endif\n\n    ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);\n\n    /*  }}} */\n\n    /*  {{{ register nginx pool cleanup hooks */\n    if (ctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n        if (cln == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        cln->handler = ngx_http_lua_request_cleanup_handler;\n        cln->data = ctx;\n        ctx->cleanup = &cln->handler;\n    }\n    /*  }}} */\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_REWRITE;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->check_client_abort) {\n        r->read_event_handler = ngx_http_lua_rd_check_broken_connection;\n\n#if (NGX_HTTP_V2)\n        if (!r->stream) {\n#endif\n\n        rev = r->connection->read;\n\n        if (!rev->active) {\n            if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n\n#if (NGX_HTTP_V2)\n        }\n#endif\n\n    } else {\n        r->read_event_handler = ngx_http_block_reading;\n    }\n\n    c = r->connection;\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(L, r, ctx, 0);\n\n    if (rc == NGX_ERROR || rc > NGX_OK) {\n        return rc;\n    }\n\n    if (rc == NGX_AGAIN) {\n        rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);\n\n    } else if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n        rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);\n    }\n\n    if (rc == NGX_OK || rc == NGX_DECLINED) {\n        if (r->header_sent\n            || (r->headers_out.status != 0 && ctx->out != NULL))\n        {\n            dd(\"header already sent\");\n\n            /* response header was already generated in rewrite_by_lua*,\n             * so it is no longer safe to proceed to later phases\n             * which may generate responses again */\n\n            if (!ctx->eof) {\n                dd(\"eof not yet sent\");\n\n                rc = ngx_http_lua_send_chain_link(r, ctx, NULL\n                                                  /* indicate last_buf */);\n                if (rc == NGX_ERROR || rc > NGX_OK) {\n                    return rc;\n                }\n            }\n\n            return NGX_HTTP_OK;\n        }\n\n        r->write_event_handler = ngx_http_core_run_phases;\n        ctx->entered_rewrite_phase = 0;\n\n        return NGX_DECLINED;\n    }\n\n    return rc;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_rewriteby.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_REWRITEBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_REWRITEBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_rewrite_handler(ngx_http_request_t *r);\nngx_int_t ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r);\nngx_int_t ngx_http_lua_rewrite_handler_file(ngx_http_request_t *r);\n\n\n#endif /* _NGX_HTTP_LUA_REWRITEBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_script.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_script.h\"\n\n\nstatic void *ngx_http_lua_script_add_code(ngx_array_t *codes, size_t size);\nstatic size_t ngx_http_lua_script_copy_len_code(\n    ngx_http_lua_script_engine_t *e);\nstatic void ngx_http_lua_script_copy_code(ngx_http_lua_script_engine_t *e);\nstatic ngx_int_t ngx_http_lua_script_add_copy_code(\n    ngx_http_lua_script_compile_t *sc, ngx_str_t *value, ngx_uint_t last);\nstatic ngx_int_t ngx_http_lua_script_compile(ngx_http_lua_script_compile_t *sc);\nstatic ngx_int_t ngx_http_lua_script_add_capture_code(\n    ngx_http_lua_script_compile_t *sc, ngx_uint_t n);\nstatic size_t ngx_http_lua_script_copy_capture_len_code(\n    ngx_http_lua_script_engine_t *e);\nstatic void ngx_http_lua_script_copy_capture_code(\n    ngx_http_lua_script_engine_t *e);\nstatic ngx_int_t ngx_http_lua_script_done(ngx_http_lua_script_compile_t *sc);\nstatic ngx_int_t ngx_http_lua_script_init_arrays(\n    ngx_http_lua_script_compile_t *sc);\n\n\nngx_int_t\nngx_http_lua_compile_complex_value(ngx_http_lua_compile_complex_value_t *ccv)\n{\n    ngx_str_t                  *v;\n    ngx_uint_t                  i, n, nv;\n    ngx_array_t                 lengths, values, *pl, *pv;\n\n    ngx_http_lua_script_compile_t   sc;\n\n    v = ccv->value;\n\n    nv = 0;\n\n    for (i = 0; i < v->len; i++) {\n        if (v->data[i] == '$') {\n            nv++;\n        }\n    }\n\n    ccv->complex_value->value = *v;\n    ccv->complex_value->lengths = NULL;\n    ccv->complex_value->values = NULL;\n\n    if (nv == 0) {\n        return NGX_OK;\n    }\n\n    n = nv * (2 * sizeof(ngx_http_lua_script_copy_code_t)\n              + sizeof(ngx_http_lua_script_capture_code_t))\n        + sizeof(uintptr_t);\n\n    if (ngx_array_init(&lengths, ccv->pool, n, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    n = (nv * (2 * sizeof(ngx_http_lua_script_copy_code_t)\n                   + sizeof(ngx_http_lua_script_capture_code_t))\n                + sizeof(uintptr_t)\n                + sizeof(uintptr_t) - 1)\n            & ~(sizeof(uintptr_t) - 1);\n\n    if (ngx_array_init(&values, ccv->pool, n, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    pl = &lengths;\n    pv = &values;\n\n    ngx_memzero(&sc, sizeof(ngx_http_lua_script_compile_t));\n\n    sc.pool = ccv->pool;\n    sc.log = ccv->log;\n    sc.source = v;\n    sc.lengths = &pl;\n    sc.values = &pv;\n    sc.complete_lengths = 1;\n    sc.complete_values = 1;\n\n    if (ngx_http_lua_script_compile(&sc) != NGX_OK) {\n        ngx_array_destroy(&lengths);\n        ngx_array_destroy(&values);\n        return NGX_ERROR;\n    }\n\n    ccv->complex_value->lengths = lengths.elts;\n    ccv->complex_value->values = values.elts;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_lua_complex_value(ngx_http_request_t *r, ngx_str_t *subj,\n    size_t offset, ngx_int_t count, int *cap,\n    ngx_http_lua_complex_value_t *val, luaL_Buffer *luabuf)\n{\n    size_t                            len;\n    u_char                           *p;\n    ngx_http_lua_script_code_pt       code;\n    ngx_http_lua_script_len_code_pt   lcode;\n    ngx_http_lua_script_engine_t      e;\n\n    if (val->lengths == NULL) {\n        luaL_addlstring(luabuf, (char *) &subj->data[offset], cap[0] - offset);\n        luaL_addlstring(luabuf, (char *) val->value.data, val->value.len);\n\n        return NGX_OK;\n    }\n\n    ngx_memzero(&e, sizeof(ngx_http_lua_script_engine_t));\n\n    e.log = r->connection->log;\n    e.ncaptures = count * 2;\n    e.captures = cap;\n    e.captures_data = subj->data;\n\n    e.ip = val->lengths;\n\n    len = 0;\n\n    while (*(uintptr_t *) e.ip) {\n        lcode = *(ngx_http_lua_script_len_code_pt *) e.ip;\n        len += lcode(&e);\n    }\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    e.ip = val->values;\n    e.pos = p;\n\n    while (*(uintptr_t *) e.ip) {\n        code = *(ngx_http_lua_script_code_pt *) e.ip;\n        code((ngx_http_lua_script_engine_t *) &e);\n    }\n\n    luaL_addlstring(luabuf, (char *) &subj->data[offset], cap[0] - offset);\n    luaL_addlstring(luabuf, (char *) p, len);\n\n    ngx_pfree(r->pool, p);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_script_compile(ngx_http_lua_script_compile_t *sc)\n{\n    u_char       ch;\n    ngx_str_t    name;\n    ngx_uint_t   i, bracket;\n    unsigned     num_var;\n    ngx_uint_t   n = 0;\n\n    if (ngx_http_lua_script_init_arrays(sc) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < sc->source->len; /* void */ ) {\n\n        name.len = 0;\n\n        if (sc->source->data[i] == '$') {\n\n            if (++i == sc->source->len) {\n                goto invalid_variable;\n            }\n\n            if (sc->source->data[i] == '$') {\n                name.data = &sc->source->data[i];\n                i++;\n                name.len++;\n\n                if (ngx_http_lua_script_add_copy_code(sc, &name,\n                                                      (i == sc->source->len))\n                    != NGX_OK)\n                {\n                    return NGX_ERROR;\n                }\n\n                continue;\n            }\n\n            if (sc->source->data[i] >= '0' && sc->source->data[i] <= '9') {\n                num_var = 1;\n                n = 0;\n\n            } else {\n                num_var = 0;\n            }\n\n            if (sc->source->data[i] == '{') {\n                bracket = 1;\n\n                if (++i == sc->source->len) {\n                    goto invalid_variable;\n                }\n\n                if (sc->source->data[i] >= '0' && sc->source->data[i] <= '9') {\n                    num_var = 1;\n                    n = 0;\n                }\n\n                name.data = &sc->source->data[i];\n\n            } else {\n                bracket = 0;\n                name.data = &sc->source->data[i];\n            }\n\n            for ( /* void */ ; i < sc->source->len; i++, name.len++) {\n                ch = sc->source->data[i];\n\n                if (ch == '}' && bracket) {\n                    i++;\n                    bracket = 0;\n                    break;\n                }\n\n                if (num_var) {\n                    if (ch >= '0' && ch <= '9') {\n                        n = n * 10 + (ch - '0');\n                        continue;\n                    }\n\n                    break;\n                }\n\n                /* not a number variable like $1, $2, etc */\n\n                if ((ch >= 'A' && ch <= 'Z')\n                    || (ch >= 'a' && ch <= 'z')\n                    || (ch >= '0' && ch <= '9')\n                    || ch == '_')\n                {\n                    continue;\n                }\n\n                break;\n            }\n\n            if (bracket) {\n                ngx_log_error(NGX_LOG_ERR, sc->log, 0,\n                              \"the closing bracket in \\\"%V\\\" \"\n                              \"variable is missing\", &name);\n                return NGX_ERROR;\n            }\n\n            if (name.len == 0) {\n                goto invalid_variable;\n            }\n\n            if (!num_var) {\n                ngx_log_error(NGX_LOG_ERR, sc->log, 0,\n                              \"attempt to use named capturing variable \"\n                              \"\\\"%V\\\" (named captures not supported yet)\",\n                              &name);\n\n                return NGX_ERROR;\n            }\n\n            sc->variables++;\n\n            if (ngx_http_lua_script_add_capture_code(sc, n) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            continue;\n        }\n\n        name.data = &sc->source->data[i];\n\n        while (i < sc->source->len) {\n\n            if (sc->source->data[i] == '$') {\n                break;\n            }\n\n            i++;\n            name.len++;\n        }\n\n        if (ngx_http_lua_script_add_copy_code(sc, &name, (i == sc->source->len))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    return ngx_http_lua_script_done(sc);\n\ninvalid_variable:\n\n    ngx_log_error(NGX_LOG_ERR, sc->log, 0,\n                  \"lua script: invalid capturing variable name found in \\\"%V\\\"\",\n                  sc->source);\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_script_add_copy_code(ngx_http_lua_script_compile_t *sc,\n    ngx_str_t *value, ngx_uint_t last)\n{\n    size_t                            size, len;\n    ngx_http_lua_script_copy_code_t  *code;\n\n    len = value->len;\n\n    code = ngx_http_lua_script_add_code(*sc->lengths,\n                                    sizeof(ngx_http_lua_script_copy_code_t));\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = (ngx_http_lua_script_code_pt) (void *)\n                 ngx_http_lua_script_copy_len_code;\n    code->len = len;\n\n    size = (sizeof(ngx_http_lua_script_copy_code_t) + len +\n            sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);\n\n    code = ngx_http_lua_script_add_code(*sc->values, size);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = ngx_http_lua_script_copy_code;\n    code->len = len;\n\n    ngx_memcpy((u_char *) code + sizeof(ngx_http_lua_script_copy_code_t),\n               value->data, value->len);\n\n    return NGX_OK;\n}\n\n\nstatic size_t\nngx_http_lua_script_copy_len_code(ngx_http_lua_script_engine_t *e)\n{\n    ngx_http_lua_script_copy_code_t  *code;\n\n    code = (ngx_http_lua_script_copy_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_lua_script_copy_code_t);\n\n    return code->len;\n}\n\n\nstatic void\nngx_http_lua_script_copy_code(ngx_http_lua_script_engine_t *e)\n{\n    u_char                           *p;\n    ngx_http_lua_script_copy_code_t  *code;\n\n    code = (ngx_http_lua_script_copy_code_t *) e->ip;\n\n    p = e->pos;\n\n    if (!e->skip) {\n        e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_lua_script_copy_code_t),\n                          code->len);\n    }\n\n    e->ip += sizeof(ngx_http_lua_script_copy_code_t)\n          + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->log, 0,\n                   \"lua script copy: \\\"%*s\\\"\", e->pos - p, p);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_script_add_capture_code(ngx_http_lua_script_compile_t *sc,\n    ngx_uint_t n)\n{\n    ngx_http_lua_script_capture_code_t  *code;\n\n    code = ngx_http_lua_script_add_code(*sc->lengths,\n                                  sizeof(ngx_http_lua_script_capture_code_t));\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = (ngx_http_lua_script_code_pt) (void *)\n                 ngx_http_lua_script_copy_capture_len_code;\n    code->n = 2 * n;\n\n    code = ngx_http_lua_script_add_code(*sc->values,\n                                  sizeof(ngx_http_lua_script_capture_code_t));\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = ngx_http_lua_script_copy_capture_code;\n    code->n = 2 * n;\n\n    return NGX_OK;\n}\n\n\nstatic size_t\nngx_http_lua_script_copy_capture_len_code(ngx_http_lua_script_engine_t *e)\n{\n    int                                  *cap;\n    ngx_uint_t                            n;\n    ngx_http_lua_script_capture_code_t   *code;\n\n    code = (ngx_http_lua_script_capture_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_lua_script_capture_code_t);\n\n    n = code->n;\n\n    if (n < e->ncaptures) {\n        cap = e->captures;\n        return cap[n + 1] - cap[n];\n    }\n\n    return 0;\n}\n\n\nstatic void\nngx_http_lua_script_copy_capture_code(ngx_http_lua_script_engine_t *e)\n{\n    int                                  *cap;\n    u_char                               *p, *pos;\n    ngx_uint_t                            n;\n    ngx_http_lua_script_capture_code_t   *code;\n\n    code = (ngx_http_lua_script_capture_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_lua_script_capture_code_t);\n\n    n = code->n;\n\n    pos = e->pos;\n\n    if (n < e->ncaptures) {\n\n        cap = e->captures;\n        p = e->captures_data;\n\n        e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]);\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->log, 0,\n                   \"lua script capture: \\\"%*s\\\"\", e->pos - pos, pos);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_script_init_arrays(ngx_http_lua_script_compile_t *sc)\n{\n    ngx_uint_t   n;\n\n    if (*sc->lengths == NULL) {\n        n = sc->variables * (2 * sizeof(ngx_http_lua_script_copy_code_t)\n                             + sizeof(ngx_http_lua_script_capture_code_t))\n            + sizeof(uintptr_t);\n\n        *sc->lengths = ngx_array_create(sc->pool, n, 1);\n        if (*sc->lengths == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (*sc->values == NULL) {\n        n = (sc->variables * (2 * sizeof(ngx_http_lua_script_copy_code_t)\n                              + sizeof(ngx_http_lua_script_capture_code_t))\n                + sizeof(uintptr_t)\n                + sizeof(uintptr_t) - 1)\n            & ~(sizeof(uintptr_t) - 1);\n\n        *sc->values = ngx_array_create(sc->pool, n, 1);\n        if (*sc->values == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    sc->variables = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_script_done(ngx_http_lua_script_compile_t *sc)\n{\n    uintptr_t   *code;\n\n    if (sc->complete_lengths) {\n        code = ngx_http_lua_script_add_code(*sc->lengths, sizeof(uintptr_t));\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n    }\n\n    if (sc->complete_values) {\n        code = ngx_http_lua_script_add_code(*sc->values, sizeof(uintptr_t));\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_lua_script_add_code(ngx_array_t *codes, size_t size)\n{\n    return ngx_array_push_n(codes, size);\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_script.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_SCRIPT_H_INCLUDED_\n#define _NGX_HTTP_LUA_SCRIPT_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\ntypedef struct {\n    ngx_log_t                  *log;\n    ngx_pool_t                 *pool;\n    ngx_str_t                  *source;\n\n    ngx_array_t               **lengths;\n    ngx_array_t               **values;\n\n    ngx_uint_t                  variables;\n\n    unsigned                    complete_lengths:1;\n    unsigned                    complete_values:1;\n} ngx_http_lua_script_compile_t;\n\n\ntypedef struct {\n    ngx_str_t                   value;\n    void                       *lengths;\n    void                       *values;\n} ngx_http_lua_complex_value_t;\n\n\ntypedef struct {\n    ngx_log_t                       *log;\n    ngx_pool_t                      *pool;\n    ngx_str_t                       *value;\n    ngx_http_lua_complex_value_t    *complex_value;\n} ngx_http_lua_compile_complex_value_t;\n\n\ntypedef struct {\n    u_char                     *ip;\n    u_char                     *pos;\n\n    ngx_str_t                   buf;\n\n    int                        *captures;\n    ngx_uint_t                  ncaptures;\n    u_char                     *captures_data;\n\n    unsigned                    skip:1;\n\n    ngx_log_t                  *log;\n} ngx_http_lua_script_engine_t;\n\n\ntypedef void (*ngx_http_lua_script_code_pt) (ngx_http_lua_script_engine_t *e);\ntypedef size_t (*ngx_http_lua_script_len_code_pt)\n    (ngx_http_lua_script_engine_t *e);\n\n\ntypedef struct {\n    ngx_http_lua_script_code_pt     code;\n    uintptr_t                       len;\n} ngx_http_lua_script_copy_code_t;\n\n\ntypedef struct {\n    ngx_http_lua_script_code_pt     code;\n    uintptr_t                       n;\n} ngx_http_lua_script_capture_code_t;\n\n\nngx_int_t ngx_http_lua_compile_complex_value(\n    ngx_http_lua_compile_complex_value_t *ccv);\nngx_int_t ngx_http_lua_complex_value(ngx_http_request_t *r, ngx_str_t *subj,\n    size_t offset, ngx_int_t count, int *cap,\n    ngx_http_lua_complex_value_t *val, luaL_Buffer *luabuf);\n\n\n#endif /* _NGX_HTTP_LUA_SCRIPT_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_semaphore.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n * Copyright (C) cuiweixie\n * I hereby assign copyright in this code to the lua-nginx-module project,\n * to be licensed under the same terms as the rest of the code.\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_semaphore.h\"\n#include \"ngx_http_lua_contentby.h\"\n\n\nngx_int_t ngx_http_lua_sema_mm_init(ngx_conf_t *cf,\n    ngx_http_lua_main_conf_t *lmcf);\nvoid ngx_http_lua_sema_mm_cleanup(void *data);\nstatic ngx_http_lua_sema_t *ngx_http_lua_alloc_sema(void);\nstatic void ngx_http_lua_free_sema(ngx_http_lua_sema_t *sem);\nstatic ngx_int_t ngx_http_lua_sema_resume(ngx_http_request_t *r);\nint ngx_http_lua_ffi_sema_new(ngx_http_lua_sema_t **psem,\n    int n, char **errmsg);\nint ngx_http_lua_ffi_sema_post(ngx_http_lua_sema_t *sem, int n);\nint ngx_http_lua_ffi_sema_wait(ngx_http_request_t *r,\n    ngx_http_lua_sema_t *sem, int wait_ms, u_char *err, size_t *errlen);\nstatic void ngx_http_lua_sema_cleanup(void *data);\nstatic void ngx_http_lua_sema_handler(ngx_event_t *ev);\nstatic void ngx_http_lua_sema_timeout_handler(ngx_event_t *ev);\nvoid ngx_http_lua_ffi_sema_gc(ngx_http_lua_sema_t *sem);\n\n\nenum {\n    SEMAPHORE_WAIT_SUCC = 0,\n    SEMAPHORE_WAIT_TIMEOUT = 1,\n};\n\n\nngx_int_t\nngx_http_lua_sema_mm_init(ngx_conf_t *cf, ngx_http_lua_main_conf_t *lmcf)\n{\n    ngx_http_lua_sema_mm_t *mm;\n\n    mm = ngx_palloc(cf->pool, sizeof(ngx_http_lua_sema_mm_t));\n    if (mm == NULL) {\n        return NGX_ERROR;\n    }\n\n    lmcf->sema_mm = mm;\n    mm->lmcf = lmcf;\n\n    ngx_queue_init(&mm->free_queue);\n    mm->cur_epoch = 0;\n    mm->total = 0;\n    mm->used = 0;\n\n    /* it's better to be 4096, but it needs some space for\n     * ngx_http_lua_sema_mm_block_t, one is enough, so it is 4095\n     */\n    mm->num_per_block = 4095;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_http_lua_sema_t *\nngx_http_lua_alloc_sema(void)\n{\n    ngx_uint_t                           i, n;\n    ngx_queue_t                         *q;\n    ngx_http_lua_sema_t                 *sem, *iter;\n    ngx_http_lua_sema_mm_t              *mm;\n    ngx_http_lua_main_conf_t            *lmcf;\n    ngx_http_lua_sema_mm_block_t        *block;\n\n    ngx_http_lua_assert(ngx_cycle && ngx_cycle->conf_ctx);\n\n    lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                               ngx_http_lua_module);\n\n    ngx_http_lua_assert(lmcf != NULL);\n\n    mm = lmcf->sema_mm;\n\n    if (!ngx_queue_empty(&mm->free_queue)) {\n        q = ngx_queue_head(&mm->free_queue);\n        ngx_queue_remove(q);\n\n        sem = ngx_queue_data(q, ngx_http_lua_sema_t, chain);\n\n        sem->block->used++;\n\n        ngx_memzero(&sem->sem_event, sizeof(ngx_event_t));\n\n        sem->sem_event.handler = ngx_http_lua_sema_handler;\n        sem->sem_event.data = sem;\n        sem->sem_event.log = ngx_cycle->log;\n\n        mm->used++;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"from head of free queue, alloc semaphore: %p\", sem);\n\n        return sem;\n    }\n\n    /* free_queue is empty */\n\n    n = sizeof(ngx_http_lua_sema_mm_block_t)\n        + mm->num_per_block * sizeof(ngx_http_lua_sema_t);\n\n    dd(\"block size: %d, item size: %d\",\n       (int) sizeof(ngx_http_lua_sema_mm_block_t),\n       (int) sizeof(ngx_http_lua_sema_t));\n\n    block = ngx_alloc(n, ngx_cycle->log);\n    if (block == NULL) {\n        return NULL;\n    }\n\n    mm->cur_epoch++;\n    mm->total += mm->num_per_block;\n    mm->used++;\n\n    block->mm = mm;\n    block->epoch = mm->cur_epoch;\n\n    sem = (ngx_http_lua_sema_t *) (block + 1);\n    sem->block = block;\n    sem->block->used = 1;\n\n    ngx_memzero(&sem->sem_event, sizeof(ngx_event_t));\n\n    sem->sem_event.handler = ngx_http_lua_sema_handler;\n    sem->sem_event.data = sem;\n    sem->sem_event.log = ngx_cycle->log;\n\n    for (iter = sem + 1, i = 1; i < mm->num_per_block; i++, iter++) {\n        iter->block = block;\n        ngx_queue_insert_tail(&mm->free_queue, &iter->chain);\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"new block, alloc semaphore: %p block: %p\", sem, block);\n\n    return sem;\n}\n\n\nvoid\nngx_http_lua_sema_mm_cleanup(void *data)\n{\n    ngx_uint_t                           i;\n    ngx_queue_t                         *q;\n    ngx_http_lua_sema_t                 *sem, *iter;\n    ngx_http_lua_sema_mm_t              *mm;\n    ngx_http_lua_main_conf_t            *lmcf;\n    ngx_http_lua_sema_mm_block_t        *block;\n\n    lmcf = (ngx_http_lua_main_conf_t *) data;\n    mm = lmcf->sema_mm;\n\n    while (!ngx_queue_empty(&mm->free_queue)) {\n        q = ngx_queue_head(&mm->free_queue);\n\n        sem = ngx_queue_data(q, ngx_http_lua_sema_t, chain);\n        block = sem->block;\n\n        ngx_http_lua_assert(block != NULL);\n\n        if (block->used == 0) {\n            iter = (ngx_http_lua_sema_t *) (block + 1);\n\n            for (i = 0; i < block->mm->num_per_block; i++, iter++) {\n                ngx_queue_remove(&iter->chain);\n            }\n\n            dd(\"free sema block: %p at final\", block);\n\n            ngx_free(block);\n\n        } else {\n            /* just return directly when some thing goes wrong */\n\n            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                          \"lua sema mm: freeing a block %p that is still \"\n                          \" used by someone\", block);\n\n            return;\n        }\n    }\n\n    dd(\"lua sema mm cleanup done\");\n}\n\n\nstatic void\nngx_http_lua_free_sema(ngx_http_lua_sema_t *sem)\n{\n    ngx_http_lua_sema_t            *iter;\n    ngx_uint_t                      i, mid_epoch;\n    ngx_http_lua_sema_mm_block_t   *block;\n    ngx_http_lua_sema_mm_t         *mm;\n\n    block = sem->block;\n    block->used--;\n\n    mm = block->mm;\n    mm->used--;\n\n    mid_epoch = mm->cur_epoch - ((mm->total / mm->num_per_block) >> 1);\n\n    if (block->epoch < mid_epoch) {\n        ngx_queue_insert_tail(&mm->free_queue, &sem->chain);\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"add to free queue tail semaphore: %p epoch: %d\"\n                       \"mid_epoch: %d cur_epoch: %d\", sem, (int) block->epoch,\n                       (int) mid_epoch, (int) mm->cur_epoch);\n\n    } else {\n        ngx_queue_insert_head(&mm->free_queue, &sem->chain);\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"add to free queue head semaphore: %p epoch: %d\"\n                       \"mid_epoch: %d cur_epoch: %d\", sem, (int) block->epoch,\n                       (int) mid_epoch, (int) mm->cur_epoch);\n    }\n\n    dd(\"used: %d\", (int) block->used);\n\n    if (block->used == 0\n        && mm->used <= (mm->total >> 1)\n        && block->epoch < mid_epoch)\n    {\n        /* load <= 50% and it's on the older side */\n        iter = (ngx_http_lua_sema_t *) (block + 1);\n\n        for (i = 0; i < mm->num_per_block; i++, iter++) {\n            ngx_queue_remove(&iter->chain);\n        }\n\n        mm->total -= mm->num_per_block;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"free semaphore block: %p\", block);\n\n        ngx_free(block);\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_lua_sema_resume(ngx_http_request_t *r)\n{\n    lua_State                   *vm;\n    ngx_connection_t            *c;\n    ngx_int_t                    rc;\n    ngx_uint_t                   nreqs;\n    ngx_http_lua_ctx_t          *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->resume_handler = ngx_http_lua_wev_handler;\n\n    c = r->connection;\n    vm = ngx_http_lua_get_lua_vm(r, ctx);\n    nreqs = c->requests;\n\n    if (ctx->cur_co_ctx->sem_resume_status == SEMAPHORE_WAIT_SUCC) {\n        lua_pushboolean(ctx->cur_co_ctx->co, 1);\n        lua_pushnil(ctx->cur_co_ctx->co);\n\n    } else {\n        lua_pushboolean(ctx->cur_co_ctx->co, 0);\n        lua_pushliteral(ctx->cur_co_ctx->co, \"timeout\");\n    }\n\n    rc = ngx_http_lua_run_thread(vm, r, ctx, 2);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run thread returned %d\", rc);\n\n    if (rc == NGX_AGAIN) {\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    /* rc == NGX_ERROR || rc >= NGX_OK */\n\n    if (ctx->entered_content_phase) {\n        ngx_http_lua_finalize_request(r, rc);\n        return NGX_DONE;\n    }\n\n    return rc;\n}\n\n\nint\nngx_http_lua_ffi_sema_new(ngx_http_lua_sema_t **psem,\n    int n, char **errmsg)\n{\n    ngx_http_lua_sema_t    *sem;\n\n    sem = ngx_http_lua_alloc_sema();\n    if (sem == NULL) {\n        *errmsg = \"no memory\";\n        return NGX_ERROR;\n    }\n\n    ngx_queue_init(&sem->wait_queue);\n\n    sem->resource_count = n;\n    sem->wait_count = 0;\n    *psem = sem;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http lua semaphore new: %p, resources: %d\",\n                   sem, sem->resource_count);\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_sema_post(ngx_http_lua_sema_t *sem, int n)\n{\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http lua semaphore post: %p, n: %d, resources: %d\",\n                   sem, n, sem->resource_count);\n\n    sem->resource_count += n;\n\n    if (!ngx_queue_empty(&sem->wait_queue)) {\n        /* we need the extra parentheses around the first argument of\n         * ngx_post_event() just to work around macro issues in nginx\n         * cores older than nginx 1.7.12 (exclusive).\n         */\n        ngx_post_event((&sem->sem_event), &ngx_posted_events);\n    }\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_sema_wait(ngx_http_request_t *r,\n    ngx_http_lua_sema_t *sem, int wait_ms, u_char *err, size_t *errlen)\n{\n    ngx_http_lua_ctx_t           *ctx;\n    ngx_http_lua_co_ctx_t        *wait_co_ctx;\n    ngx_int_t                     rc;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http lua semaphore wait: %p, timeout: %d, \"\n                   \"resources: %d, event posted: %d\",\n                   sem, wait_ms, sem->resource_count,\n#if (nginx_version >= 1007005)\n                   (int) sem->sem_event.posted\n#else\n                   sem->sem_event.prev ? 1 : 0\n#endif\n                   );\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        *errlen = ngx_snprintf(err, *errlen, \"no request ctx found\") - err;\n        return NGX_ERROR;\n    }\n\n    rc = ngx_http_lua_ffi_check_context(ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE,\n                                        err, errlen);\n\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    /* we keep the order, will first resume the thread waiting for the\n     * longest time in ngx_http_lua_sema_handler\n     */\n\n    if (ngx_queue_empty(&sem->wait_queue) && sem->resource_count > 0) {\n        sem->resource_count--;\n        return NGX_OK;\n    }\n\n    if (wait_ms == 0) {\n        return NGX_DECLINED;\n    }\n\n    sem->wait_count++;\n    wait_co_ctx = ctx->cur_co_ctx;\n\n    wait_co_ctx->sleep.handler = ngx_http_lua_sema_timeout_handler;\n    wait_co_ctx->sleep.data = ctx->cur_co_ctx;\n    wait_co_ctx->sleep.log = r->connection->log;\n\n    ngx_add_timer(&wait_co_ctx->sleep, (ngx_msec_t) wait_ms);\n\n    dd(\"ngx_http_lua_ffi_sema_wait add timer coctx:%p wait: %d(ms)\",\n       wait_co_ctx, wait_ms);\n\n    ngx_queue_insert_tail(&sem->wait_queue, &wait_co_ctx->sem_wait_queue);\n\n    wait_co_ctx->data = sem;\n    wait_co_ctx->cleanup = ngx_http_lua_sema_cleanup;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http lua semaphore wait yielding\");\n\n    return NGX_AGAIN;\n}\n\n\nint\nngx_http_lua_ffi_sema_count(ngx_http_lua_sema_t *sem)\n{\n    return sem->resource_count - sem->wait_count;\n}\n\n\nstatic void\nngx_http_lua_sema_cleanup(void *data)\n{\n    ngx_http_lua_co_ctx_t          *coctx = data;\n    ngx_queue_t                    *q;\n    ngx_http_lua_sema_t            *sem;\n\n    sem = coctx->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http lua semaphore cleanup\");\n\n    if (coctx->sleep.timer_set) {\n        ngx_del_timer(&coctx->sleep);\n    }\n\n    q = &coctx->sem_wait_queue;\n\n    ngx_queue_remove(q);\n    sem->wait_count--;\n    coctx->cleanup = NULL;\n}\n\n\nstatic void\nngx_http_lua_sema_handler(ngx_event_t *ev)\n{\n    ngx_http_lua_sema_t         *sem;\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *wait_co_ctx;\n    ngx_connection_t            *c;\n    ngx_queue_t                 *q;\n\n    sem = ev->data;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"semaphore handler: wait queue: %sempty, resource count: %d\",\n                   ngx_queue_empty(&sem->wait_queue) ? \"\" : \"not \",\n                   sem->resource_count);\n    while (!ngx_queue_empty(&sem->wait_queue) && sem->resource_count > 0) {\n        q = ngx_queue_head(&sem->wait_queue);\n        ngx_queue_remove(q);\n\n        sem->wait_count--;\n\n        wait_co_ctx = ngx_queue_data(q, ngx_http_lua_co_ctx_t, sem_wait_queue);\n        wait_co_ctx->cleanup = NULL;\n\n        if (wait_co_ctx->sleep.timer_set) {\n            ngx_del_timer(&wait_co_ctx->sleep);\n        }\n\n        r = ngx_http_lua_get_req(wait_co_ctx->co);\n        c = r->connection;\n\n        ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n        ngx_http_lua_assert(ctx != NULL);\n\n        sem->resource_count--;\n\n        ctx->cur_co_ctx = wait_co_ctx;\n\n        wait_co_ctx->sem_resume_status = SEMAPHORE_WAIT_SUCC;\n\n        if (ctx->entered_content_phase) {\n            (void) ngx_http_lua_sema_resume(r);\n\n        } else {\n            ctx->resume_handler = ngx_http_lua_sema_resume;\n            ngx_http_core_run_phases(r);\n        }\n\n        ngx_http_run_posted_requests(c);\n    }\n}\n\n\nstatic void\nngx_http_lua_sema_timeout_handler(ngx_event_t *ev)\n{\n    ngx_http_lua_co_ctx_t       *wait_co_ctx;\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_connection_t            *c;\n    ngx_http_lua_sema_t         *sem;\n\n    wait_co_ctx = ev->data;\n    wait_co_ctx->cleanup = NULL;\n\n    dd(\"ngx_http_lua_sema_timeout_handler timeout coctx:%p\", wait_co_ctx);\n\n    sem = wait_co_ctx->data;\n\n    ngx_queue_remove(&wait_co_ctx->sem_wait_queue);\n    sem->wait_count--;\n\n    r = ngx_http_lua_get_req(wait_co_ctx->co);\n    c = r->connection;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    ngx_http_lua_assert(ctx != NULL);\n\n    ctx->cur_co_ctx = wait_co_ctx;\n\n    wait_co_ctx->sem_resume_status = SEMAPHORE_WAIT_TIMEOUT;\n\n    if (ctx->entered_content_phase) {\n        (void) ngx_http_lua_sema_resume(r);\n\n    } else {\n        ctx->resume_handler = ngx_http_lua_sema_resume;\n        ngx_http_core_run_phases(r);\n    }\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nvoid\nngx_http_lua_ffi_sema_gc(ngx_http_lua_sema_t *sem)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"in lua gc, semaphore %p\", sem);\n\n    if (sem == NULL) {\n        return;\n    }\n\n    if (!ngx_terminate\n        && !ngx_quit\n        && !ngx_queue_empty(&sem->wait_queue))\n    {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"in lua semaphore gc wait queue is\"\n                      \" not empty while the semaphore %p is being \"\n                      \"destroyed\", sem);\n    }\n\n    if (sem->sem_event.posted) {\n        ngx_delete_posted_event(&sem->sem_event);\n    }\n\n    ngx_http_lua_free_sema(sem);\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_semaphore.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n * Copyright (C) cuiweixie\n * I hereby assign copyright in this code to the lua-nginx-module project,\n * to be licensed under the same terms as the rest of the code.\n */\n\n\n#ifndef _NGX_HTTP_LUA_SEMAPHORE_H_INCLUDED_\n#define _NGX_HTTP_LUA_SEMAPHORE_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\ntypedef struct ngx_http_lua_sema_mm_block_s {\n    ngx_uint_t                       used;\n    ngx_http_lua_sema_mm_t          *mm;\n    ngx_uint_t                       epoch;\n} ngx_http_lua_sema_mm_block_t;\n\n\nstruct ngx_http_lua_sema_mm_s {\n    ngx_queue_t                  free_queue;\n    ngx_uint_t                   total;\n    ngx_uint_t                   used;\n    ngx_uint_t                   num_per_block;\n    ngx_uint_t                   cur_epoch;\n    ngx_http_lua_main_conf_t    *lmcf;\n};\n\n\ntypedef struct ngx_http_lua_sema_s {\n    ngx_queue_t                          wait_queue;\n    ngx_queue_t                          chain;\n    ngx_event_t                          sem_event;\n    ngx_http_lua_sema_mm_block_t        *block;\n    int                                  resource_count;\n    unsigned                             wait_count;\n} ngx_http_lua_sema_t;\n\n\nvoid ngx_http_lua_sema_mm_cleanup(void *data);\nngx_int_t ngx_http_lua_sema_mm_init(ngx_conf_t *cf,\n    ngx_http_lua_main_conf_t *lmcf);\n\n\n#endif /* _NGX_HTTP_LUA_SEMAPHORE_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_server_rewriteby.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n#include <nginx.h>\n#include \"ngx_http_lua_server_rewriteby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_exception.h\"\n#include \"ngx_http_lua_cache.h\"\n\nstatic ngx_int_t ngx_http_lua_server_rewrite_by_chunk(lua_State *L,\n    ngx_http_request_t *r);\n\nngx_int_t\nngx_http_lua_server_rewrite_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                    rc;\n    lua_State                   *L;\n    ngx_http_lua_srv_conf_t     *lscf;\n    ngx_http_lua_loc_conf_t     *llcf;\n    ngx_http_lua_ctx_t          *ctx;\n\n    /* XXX we need to take into account ngx_rewrite's location dump */\n    if (r->uri_changed) {\n        return NGX_DECLINED;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua server rewrite handler, uri:\\\"%V\\\" c:%ud\", &r->uri,\n                    r->main->count);\n\n    lscf = ngx_http_get_module_srv_conf(r, ngx_http_lua_module);\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    if (lscf->srv.server_rewrite_handler == NULL) {\n        dd(\"no rewrite handler found\");\n        return NGX_DECLINED;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    dd(\"ctx = %p\", ctx);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    dd(\"entered? %d\", (int) ctx->entered_server_rewrite_phase);\n\n    if (ctx->entered_server_rewrite_phase) {\n        dd(\"rewriteby: calling wev handler\");\n        rc = ctx->resume_handler(r);\n        dd(\"rewriteby: wev handler returns %d\", (int) rc);\n\n        if (rc == NGX_OK) {\n            rc = NGX_DECLINED;\n        }\n\n        if (rc == NGX_DECLINED) {\n            if (r->header_sent\n                || (r->headers_out.status != 0 && ctx->out != NULL))\n            {\n                dd(\"header already sent\");\n\n                /* response header was already generated in rewrite_by_lua*,\n                 * so it is no longer safe to proceed to later phases\n                 * which may generate responses again */\n\n                if (!ctx->eof) {\n                    dd(\"eof not yet sent\");\n\n                    rc = ngx_http_lua_send_chain_link(r, ctx, NULL\n                                                    /* indicate last_buf */);\n                    if (rc == NGX_ERROR || rc > NGX_OK) {\n                        return rc;\n                    }\n                }\n\n                return NGX_HTTP_OK;\n            }\n\n            r->write_event_handler = ngx_http_core_run_phases;\n            ctx->entered_server_rewrite_phase = 0;\n\n            return NGX_DECLINED;\n        }\n\n        return rc;\n    }\n\n    if (ctx->waiting_more_body) {\n        return NGX_DONE;\n    }\n\n    /* TODO: lscf do not have force_read_body */\n    if (llcf->force_read_body && !ctx->read_body_done) {\n        r->request_body_in_single_buf = 1;\n        r->request_body_in_persistent_file = 1;\n        r->request_body_in_clean_file = 1;\n\n        rc = ngx_http_read_client_request_body(r,\n                                          ngx_http_lua_generic_phase_post_read);\n\n        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n\n        if (rc == NGX_AGAIN) {\n            ctx->waiting_more_body = 1;\n            return NGX_DONE;\n        }\n    }\n\n    dd(\"calling server rewrite handler\");\n    return lscf->srv.server_rewrite_handler(r, lscf, L);\n}\n\n\nngx_int_t\nngx_http_lua_server_rewrite_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L)\n{\n    ngx_int_t                    rc;\n\n    dd(\"server_rewrite by lua inline\");\n\n\n    /*  load Lua inline script (w/ cache) sp = 1 */\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       lscf->srv.server_rewrite_src.value.data,\n                                       lscf->srv.server_rewrite_src.value.len,\n                                       &lscf->srv.server_rewrite_src_ref,\n                                       lscf->srv.server_rewrite_src_key,\n                                       (const char *)\n                                       lscf->srv.server_rewrite_chunkname);\n    if (rc != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    return ngx_http_lua_server_rewrite_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_server_rewrite_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L)\n{\n    ngx_int_t                        rc;\n    u_char                          *script_path;\n    ngx_str_t                        eval_src;\n\n\n    if (ngx_http_complex_value(r, &lscf->srv.server_rewrite_src,\n                               &eval_src) != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,\n        eval_src.len);\n\n    if (script_path == NULL) {\n        return NGX_ERROR;\n    }\n\n    /*  load Lua script file (w/ cache)        sp = 1 */\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,\n                                     &lscf->srv.server_rewrite_src_ref,\n                                     lscf->srv.server_rewrite_src_key);\n    if (rc != NGX_OK) {\n        if (rc < NGX_HTTP_SPECIAL_RESPONSE) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        return rc;\n    }\n\n    return ngx_http_lua_server_rewrite_by_chunk(L, r);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_server_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    int                      co_ref;\n    lua_State               *co;\n    ngx_int_t                rc;\n    ngx_uint_t               nreqs;\n    ngx_event_t             *rev;\n    ngx_connection_t        *c;\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_pool_cleanup_t      *cln;\n\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    /*  {{{ new coroutine to handle request */\n    co = ngx_http_lua_new_thread(r, L, &co_ref);\n\n    if (co == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"lua: failed to create new coroutine to handle request\");\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    /*  move code closure to new coroutine */\n    lua_xmove(L, co, 1);\n\n#ifndef OPENRESTY_LUAJIT\n    /*  set closure's env table to new coroutine's globals table */\n    ngx_http_lua_get_globals_table(co);\n    lua_setfenv(co, -2);\n#endif\n\n    /*  save nginx request in coroutine globals table */\n    ngx_http_lua_set_req(co, r);\n\n    /*  {{{ initialize request context */\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    dd(\"ctx = %p\", ctx);\n\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_lua_reset_ctx(r, L, ctx);\n\n    ctx->entered_server_rewrite_phase = 1;\n\n    ctx->cur_co_ctx = &ctx->entry_co_ctx;\n    ctx->cur_co_ctx->co = co;\n    ctx->cur_co_ctx->co_ref = co_ref;\n#ifdef NGX_LUA_USE_ASSERT\n    ctx->cur_co_ctx->co_top = 1;\n#endif\n\n    ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);\n\n    /*  }}} */\n\n    /*  {{{ register request cleanup hooks */\n    if (ctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n        if (cln == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        cln->handler = ngx_http_lua_request_cleanup_handler;\n        cln->data = ctx;\n        ctx->cleanup = &cln->handler;\n    }\n    /*  }}} */\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->check_client_abort) {\n        r->read_event_handler = ngx_http_lua_rd_check_broken_connection;\n\n#if (NGX_HTTP_V2)\n        if (!r->stream) {\n#endif\n\n        rev = r->connection->read;\n\n        if (!rev->active) {\n            if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n\n#if (NGX_HTTP_V2)\n        }\n#endif\n\n    } else {\n        r->read_event_handler = ngx_http_block_reading;\n    }\n\n    c = r->connection;\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(L, r, ctx, 0);\n\n    if (rc == NGX_ERROR || rc > NGX_OK) {\n        return rc;\n    }\n\n    if (rc == NGX_AGAIN) {\n        rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);\n\n    } else if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n        rc = ngx_http_lua_run_posted_threads(c, L, r, ctx, nreqs);\n    }\n\n    if (rc == NGX_OK || rc == NGX_DECLINED) {\n        if (r->header_sent\n            || (r->headers_out.status != 0 && ctx->out != NULL))\n        {\n            dd(\"header already sent\");\n\n            /* response header was already generated in rewrite_by_lua*,\n             * so it is no longer safe to proceed to later phases\n             * which may generate responses again */\n\n            if (!ctx->eof) {\n                dd(\"eof not yet sent\");\n\n                rc = ngx_http_lua_send_chain_link(r, ctx, NULL\n                                                  /* indicate last_buf */);\n                if (rc == NGX_ERROR || rc > NGX_OK) {\n                    return rc;\n                }\n            }\n\n            return NGX_HTTP_OK;\n        }\n\n        r->write_event_handler = ngx_http_core_run_phases;\n        ctx->entered_server_rewrite_phase = 0;\n\n        return NGX_DECLINED;\n    }\n\n    return rc;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_server_rewriteby.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n#ifndef _NGX_HTTP_LUA_SERVER_REWRITEBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_SERVER_REWRITEBY_H_INCLUDED_\n\n#include \"ngx_http_lua_common.h\"\n\nngx_int_t ngx_http_lua_server_rewrite_handler(ngx_http_request_t *r);\nngx_int_t ngx_http_lua_server_rewrite_handler_inline(ngx_http_request_t *r,\n   ngx_http_lua_srv_conf_t *lscf, lua_State *L);\nngx_int_t ngx_http_lua_server_rewrite_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L);\n\n#endif /* _NGX_HTTP_LUA_SERVER_REWRITEBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_setby.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_setby.h\"\n#include \"ngx_http_lua_exception.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_pcrefix.h\"\n#include \"ngx_http_lua_log.h\"\n#include \"ngx_http_lua_string.h\"\n#include \"ngx_http_lua_misc.h\"\n#include \"ngx_http_lua_consts.h\"\n#include \"ngx_http_lua_shdict.h\"\n#include \"ngx_http_lua_util.h\"\n\n\nstatic void ngx_http_lua_set_by_lua_env(lua_State *L, ngx_http_request_t *r,\n    size_t nargs, ngx_http_variable_value_t *args);\n\n\nngx_int_t\nngx_http_lua_set_by_chunk(lua_State *L, ngx_http_request_t *r, ngx_str_t *val,\n    ngx_http_variable_value_t *args, size_t nargs, ngx_str_t *script)\n{\n    size_t           i;\n    ngx_int_t        rc;\n    u_char          *err_msg;\n    size_t           len;\n    u_char          *data;\n#if (NGX_PCRE)\n    ngx_pool_t      *old_pool;\n#endif\n\n    dd(\"nargs: %d\", (int) nargs);\n\n    dd(\"set Lua VM panic handler\");\n\n    lua_atpanic(L, ngx_http_lua_atpanic);\n\n    NGX_LUA_EXCEPTION_TRY {\n        dd(\"initialize nginx context in Lua VM, code chunk at \"\n           \"stack top    sp = 1\");\n        ngx_http_lua_set_by_lua_env(L, r, nargs, args);\n\n        /*  passing directive arguments to the user code */\n        for (i = 0; i < nargs; i++) {\n            lua_pushlstring(L, (const char *) args[i].data, args[i].len);\n        }\n\n#if (NGX_PCRE)\n        /* XXX: work-around to nginx regex subsystem */\n        old_pool = ngx_http_lua_pcre_malloc_init(r->pool);\n#endif\n\n        lua_pushcfunction(L, ngx_http_lua_traceback);\n        lua_insert(L, 1);  /* put it under chunk and args */\n\n        dd(\"protected call user code\");\n\n        rc = lua_pcall(L, nargs, 1, 1);\n\n        dd(\"after protected call user code\");\n\n        lua_remove(L, 1);  /* remove traceback function */\n\n#if (NGX_PCRE)\n        /* XXX: work-around to nginx regex subsystem */\n        ngx_http_lua_pcre_malloc_done(old_pool);\n#endif\n\n        if (rc != 0) {\n            /*  error occurred when running loaded code */\n            err_msg = (u_char *) lua_tolstring(L, -1, &len);\n\n            if (err_msg == NULL) {\n                err_msg = (u_char *) \"unknown reason\";\n                len = sizeof(\"unknown reason\") - 1;\n            }\n\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"failed to run set_by_lua*: %*s\", len, err_msg);\n\n            lua_settop(L, 0);    /*  clear remaining elems on stack */\n\n            return NGX_ERROR;\n        }\n\n        data = (u_char *) lua_tolstring(L, -1, &len);\n\n        if (data) {\n            val->data = ngx_palloc(r->pool, len);\n            if (val->data == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(val->data, data, len);\n            val->len = len;\n\n        } else {\n            val->data = NULL;\n            val->len = 0;\n        }\n\n    } NGX_LUA_EXCEPTION_CATCH {\n\n        dd(\"nginx execution restored\");\n        return NGX_ERROR;\n    }\n\n    /*  clear Lua stack */\n    lua_settop(L, 0);\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_lua_ffi_get_setby_param(ngx_http_request_t *r, int idx,\n    u_char **data_p, size_t *len_p)\n{\n    int         n;\n\n    ngx_http_variable_value_t       *v;\n    ngx_http_lua_main_conf_t        *lmcf;\n\n    idx--;\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    /*  get number of args from lmcf */\n    n = lmcf->setby_nargs;\n\n    /*  get args from lmcf */\n    v = lmcf->setby_args;\n\n    if (idx < 0 || idx > n - 1) {\n        *len_p = 0;\n\n    } else {\n        *data_p = v[idx].data;\n        *len_p = v[idx].len;\n    }\n}\n\n\n/**\n * Set environment table for the given code closure.\n *\n * Before:\n *         | code closure | <- top\n *         |      ...     |\n *\n * After:\n *         | code closure | <- top\n *         |      ...     |\n * */\nstatic void\nngx_http_lua_set_by_lua_env(lua_State *L, ngx_http_request_t *r, size_t nargs,\n    ngx_http_variable_value_t *args)\n{\n    ngx_http_lua_main_conf_t        *lmcf;\n\n    ngx_http_lua_set_req(L, r);\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    lmcf->setby_nargs = nargs;\n    lmcf->setby_args = args;\n\n#ifndef OPENRESTY_LUAJIT\n    /**\n     * we want to create empty environment for current script\n     *\n     * newt = {}\n     * newt[\"_G\"] = newt\n     * setmetatable(newt, {__index = _G})\n     *\n     * if a function or symbol is not defined in our env, __index will lookup\n     * in the global env.\n     *\n     * all variables created in the script-env will be thrown away at the end\n     * of the script run.\n     * */\n    ngx_http_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);\n\n    /*  {{{ make new env inheriting main thread's globals table */\n    /* the metatable for the new env */\n    lua_createtable(L, 0 /* narr */, 1 /* nrec */);\n    ngx_http_lua_get_globals_table(L);\n    lua_setfield(L, -2, \"__index\");\n    lua_setmetatable(L, -2);    /*  setmetatable(newt, {__index = _G}) */\n    /*  }}} */\n\n    lua_setfenv(L, -2);    /*  set new running env for the code closure */\n#endif\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_setby.h",
    "content": "#ifndef _NGX_HTTP_LUA_SET_BY_H_INCLUDED_\n#define _NGX_HTTP_LUA_SET_BY_H_INCLUDED_\n\n#include \"ngx_http_lua_common.h\"\n\n\nngx_int_t ngx_http_lua_set_by_chunk(lua_State *L, ngx_http_request_t *r,\n    ngx_str_t *val, ngx_http_variable_value_t *args, size_t nargs,\n    ngx_str_t *script);\n\n\n#endif /* _NGX_HTTP_LUA_SET_BY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_shdict.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_shdict.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_api.h\"\n\n\nstatic int ngx_http_lua_shdict_expire(ngx_http_lua_shdict_ctx_t *ctx,\n    ngx_uint_t n);\nstatic ngx_int_t ngx_http_lua_shdict_lookup(ngx_shm_zone_t *shm_zone,\n    ngx_uint_t hash, u_char *kdata, size_t klen,\n    ngx_http_lua_shdict_node_t **sdp);\nstatic int ngx_http_lua_shdict_flush_expired(lua_State *L);\nstatic int ngx_http_lua_shdict_get_keys(lua_State *L);\nstatic int ngx_http_lua_shdict_lpush(lua_State *L);\nstatic int ngx_http_lua_shdict_rpush(lua_State *L);\nstatic int ngx_http_lua_shdict_push_helper(lua_State *L, int flags);\nstatic int ngx_http_lua_shdict_lpop(lua_State *L);\nstatic int ngx_http_lua_shdict_rpop(lua_State *L);\nstatic int ngx_http_lua_shdict_pop_helper(lua_State *L, int flags);\nstatic int ngx_http_lua_shdict_llen(lua_State *L);\n\n\nstatic ngx_inline ngx_shm_zone_t *ngx_http_lua_shdict_get_zone(lua_State *L,\n    int index);\n\n\n#define NGX_HTTP_LUA_SHDICT_ADD         0x0001\n#define NGX_HTTP_LUA_SHDICT_REPLACE     0x0002\n#define NGX_HTTP_LUA_SHDICT_SAFE_STORE  0x0004\n\n\n#define NGX_HTTP_LUA_SHDICT_LEFT        0x0001\n#define NGX_HTTP_LUA_SHDICT_RIGHT       0x0002\n\n\nenum {\n    SHDICT_USERDATA_INDEX = 1,\n};\n\n\nenum {\n    SHDICT_TNIL = 0,        /* same as LUA_TNIL */\n    SHDICT_TBOOLEAN = 1,    /* same as LUA_TBOOLEAN */\n    SHDICT_TNUMBER = 3,     /* same as LUA_TNUMBER */\n    SHDICT_TSTRING = 4,     /* same as LUA_TSTRING */\n    SHDICT_TLIST = 5,\n};\n\n\nstatic ngx_inline ngx_queue_t *\nngx_http_lua_shdict_get_list_head(ngx_http_lua_shdict_node_t *sd, size_t len)\n{\n    return (ngx_queue_t *) ngx_align_ptr(((u_char *) &sd->data + len),\n                                         NGX_ALIGNMENT);\n}\n\n\nngx_int_t\nngx_http_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data)\n{\n    ngx_http_lua_shdict_ctx_t  *octx = data;\n\n    size_t                      len;\n    ngx_http_lua_shdict_ctx_t  *ctx;\n\n    dd(\"init zone\");\n\n    ctx = shm_zone->data;\n\n    if (octx) {\n        ctx->sh = octx->sh;\n        ctx->shpool = octx->shpool;\n\n        return NGX_OK;\n    }\n\n    ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n\n    if (shm_zone->shm.exists) {\n        ctx->sh = ctx->shpool->data;\n\n        return NGX_OK;\n    }\n\n    ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_lua_shdict_shctx_t));\n    if (ctx->sh == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->shpool->data = ctx->sh;\n\n    ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,\n                    ngx_http_lua_shdict_rbtree_insert_value);\n\n    ngx_queue_init(&ctx->sh->lru_queue);\n\n    len = sizeof(\" in lua_shared_dict zone \\\"\\\"\") + shm_zone->shm.name.len;\n\n    ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);\n    if (ctx->shpool->log_ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_sprintf(ctx->shpool->log_ctx, \" in lua_shared_dict zone \\\"%V\\\"%Z\",\n                &shm_zone->shm.name);\n\n    ctx->shpool->log_nomem = 0;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_lua_shdict_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)\n{\n    ngx_rbtree_node_t           **p;\n    ngx_http_lua_shdict_node_t   *sdn, *sdnt;\n\n    for ( ;; ) {\n\n        if (node->key < temp->key) {\n\n            p = &temp->left;\n\n        } else if (node->key > temp->key) {\n\n            p = &temp->right;\n\n        } else { /* node->key == temp->key */\n\n            sdn = (ngx_http_lua_shdict_node_t *) &node->color;\n            sdnt = (ngx_http_lua_shdict_node_t *) &temp->color;\n\n            p = ngx_memn2cmp(sdn->data, sdnt->data, sdn->key_len,\n                             sdnt->key_len) < 0 ? &temp->left : &temp->right;\n        }\n\n        if (*p == sentinel) {\n            break;\n        }\n\n        temp = *p;\n    }\n\n    *p = node;\n    node->parent = temp;\n    node->left = sentinel;\n    node->right = sentinel;\n    ngx_rbt_red(node);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, ngx_uint_t hash,\n    u_char *kdata, size_t klen, ngx_http_lua_shdict_node_t **sdp)\n{\n    ngx_int_t                    rc;\n    ngx_time_t                  *tp;\n    uint64_t                     now;\n    int64_t                      ms;\n    ngx_rbtree_node_t           *node, *sentinel;\n    ngx_http_lua_shdict_ctx_t   *ctx;\n    ngx_http_lua_shdict_node_t  *sd;\n\n    ctx = shm_zone->data;\n\n    node = ctx->sh->rbtree.root;\n    sentinel = ctx->sh->rbtree.sentinel;\n\n    while (node != sentinel) {\n\n        if (hash < node->key) {\n            node = node->left;\n            continue;\n        }\n\n        if (hash > node->key) {\n            node = node->right;\n            continue;\n        }\n\n        /* hash == node->key */\n\n        sd = (ngx_http_lua_shdict_node_t *) &node->color;\n\n        rc = ngx_memn2cmp(kdata, sd->data, klen, (size_t) sd->key_len);\n\n        if (rc == 0) {\n            *sdp = sd;\n\n            dd(\"node expires: %lld\", (long long) sd->expires);\n\n            if (sd->expires != 0) {\n                tp = ngx_timeofday();\n\n                now = (uint64_t) tp->sec * 1000 + tp->msec;\n                ms = sd->expires - now;\n\n                dd(\"time to live: %lld\", (long long) ms);\n\n                if (ms <= 0) {\n                    dd(\"node already expired\");\n                    return NGX_DONE;\n                }\n            }\n\n            ngx_queue_remove(&sd->queue);\n            ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);\n\n            return NGX_OK;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    *sdp = NULL;\n\n    return NGX_DECLINED;\n}\n\n\nstatic int\nngx_http_lua_shdict_expire(ngx_http_lua_shdict_ctx_t *ctx, ngx_uint_t n)\n{\n    ngx_time_t                      *tp;\n    uint64_t                         now;\n    ngx_queue_t                     *q, *list_queue, *lq;\n    int64_t                          ms;\n    ngx_rbtree_node_t               *node;\n    ngx_http_lua_shdict_node_t      *sd;\n    int                              freed = 0;\n    ngx_http_lua_shdict_list_node_t *lnode;\n\n    tp = ngx_timeofday();\n\n    now = (uint64_t) tp->sec * 1000 + tp->msec;\n\n    /*\n     * n == 1 deletes one or two expired entries\n     * n == 0 deletes oldest entry by force\n     *        and one or two zero rate entries\n     */\n\n    while (n < 3) {\n\n        if (ngx_queue_empty(&ctx->sh->lru_queue)) {\n            return freed;\n        }\n\n        q = ngx_queue_last(&ctx->sh->lru_queue);\n\n        sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);\n\n        if (n++ != 0) {\n\n            if (sd->expires == 0) {\n                return freed;\n            }\n\n            ms = sd->expires - now;\n            if (ms > 0) {\n                return freed;\n            }\n        }\n\n        if (sd->value_type == SHDICT_TLIST) {\n            list_queue = ngx_http_lua_shdict_get_list_head(sd, sd->key_len);\n\n            for (lq = ngx_queue_head(list_queue);\n                 lq != ngx_queue_sentinel(list_queue);\n                 lq = ngx_queue_next(lq))\n            {\n                lnode = ngx_queue_data(lq, ngx_http_lua_shdict_list_node_t,\n                                       queue);\n\n                ngx_slab_free_locked(ctx->shpool, lnode);\n            }\n        }\n\n        ngx_queue_remove(q);\n\n        node = (ngx_rbtree_node_t *)\n                   ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));\n\n        ngx_rbtree_delete(&ctx->sh->rbtree, node);\n\n        ngx_slab_free_locked(ctx->shpool, node);\n\n        freed++;\n    }\n\n    return freed;\n}\n\n\nvoid\nngx_http_lua_inject_shdict_api(ngx_http_lua_main_conf_t *lmcf, lua_State *L)\n{\n    ngx_http_lua_shdict_ctx_t   *ctx;\n    ngx_uint_t                   i;\n    ngx_shm_zone_t             **zone;\n    ngx_shm_zone_t             **zone_udata;\n\n    if (lmcf->shdict_zones != NULL) {\n        lua_createtable(L, 0, lmcf->shdict_zones->nelts /* nrec */);\n                /* ngx.shared */\n\n        lua_createtable(L, 0 /* narr */, 22 /* nrec */); /* shared mt */\n\n        lua_pushcfunction(L, ngx_http_lua_shdict_lpush);\n        lua_setfield(L, -2, \"lpush\");\n\n        lua_pushcfunction(L, ngx_http_lua_shdict_rpush);\n        lua_setfield(L, -2, \"rpush\");\n\n        lua_pushcfunction(L, ngx_http_lua_shdict_lpop);\n        lua_setfield(L, -2, \"lpop\");\n\n        lua_pushcfunction(L, ngx_http_lua_shdict_rpop);\n        lua_setfield(L, -2, \"rpop\");\n\n        lua_pushcfunction(L, ngx_http_lua_shdict_llen);\n        lua_setfield(L, -2, \"llen\");\n\n        lua_pushcfunction(L, ngx_http_lua_shdict_flush_expired);\n        lua_setfield(L, -2, \"flush_expired\");\n\n        lua_pushcfunction(L, ngx_http_lua_shdict_get_keys);\n        lua_setfield(L, -2, \"get_keys\");\n\n        lua_pushvalue(L, -1); /* shared mt mt */\n        lua_setfield(L, -2, \"__index\"); /* shared mt */\n\n        zone = lmcf->shdict_zones->elts;\n\n        for (i = 0; i < lmcf->shdict_zones->nelts; i++) {\n            ctx = zone[i]->data;\n\n            lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len);\n                /* shared mt key */\n\n            lua_createtable(L, 1 /* narr */, 0 /* nrec */);\n                /* table of zone[i] */\n            zone_udata = lua_newuserdata(L, sizeof(ngx_shm_zone_t *));\n                /* shared mt key ud */\n            *zone_udata = zone[i];\n            lua_rawseti(L, -2, SHDICT_USERDATA_INDEX); /* {zone[i]} */\n            lua_pushvalue(L, -3); /* shared mt key ud mt */\n            lua_setmetatable(L, -2); /* shared mt key ud */\n            lua_rawset(L, -4); /* shared mt */\n        }\n\n        lua_pop(L, 1); /* shared */\n\n    } else {\n        lua_newtable(L);    /* ngx.shared */\n    }\n\n    lua_setfield(L, -2, \"shared\");\n}\n\n\nstatic ngx_inline ngx_shm_zone_t *\nngx_http_lua_shdict_get_zone(lua_State *L, int index)\n{\n    ngx_shm_zone_t      *zone;\n    ngx_shm_zone_t     **zone_udata;\n\n    lua_rawgeti(L, index, SHDICT_USERDATA_INDEX);\n    zone_udata = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    if (zone_udata == NULL) {\n        return NULL;\n    }\n\n    zone = *zone_udata;\n    return zone;\n}\n\n\nstatic int\nngx_http_lua_shdict_flush_expired(lua_State *L)\n{\n    ngx_queue_t                     *q, *prev, *list_queue, *lq;\n    ngx_http_lua_shdict_node_t      *sd;\n    ngx_http_lua_shdict_ctx_t       *ctx;\n    ngx_shm_zone_t                  *zone;\n    ngx_time_t                      *tp;\n    int                              freed = 0;\n    int                              attempts = 0;\n    ngx_rbtree_node_t               *node;\n    uint64_t                         now;\n    int                              n;\n    ngx_http_lua_shdict_list_node_t *lnode;\n\n    n = lua_gettop(L);\n\n    if (n != 1 && n != 2) {\n        return luaL_error(L, \"expecting 1 or 2 argument(s), but saw %d\", n);\n    }\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    zone = ngx_http_lua_shdict_get_zone(L, 1);\n    if (zone == NULL) {\n        return luaL_error(L, \"bad user data for the ngx_shm_zone_t pointer\");\n    }\n\n    if (n == 2) {\n        attempts = luaL_checkint(L, 2);\n    }\n\n    ctx = zone->data;\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n\n    if (ngx_queue_empty(&ctx->sh->lru_queue)) {\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n        lua_pushnumber(L, 0);\n        return 1;\n    }\n\n    tp = ngx_timeofday();\n\n    now = (uint64_t) tp->sec * 1000 + tp->msec;\n\n    q = ngx_queue_last(&ctx->sh->lru_queue);\n\n    while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) {\n        prev = ngx_queue_prev(q);\n\n        sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);\n\n        if (sd->expires != 0 && sd->expires <= now) {\n\n            if (sd->value_type == SHDICT_TLIST) {\n                list_queue = ngx_http_lua_shdict_get_list_head(sd, sd->key_len);\n\n                for (lq = ngx_queue_head(list_queue);\n                     lq != ngx_queue_sentinel(list_queue);\n                     lq = ngx_queue_next(lq))\n                {\n                    lnode = ngx_queue_data(lq, ngx_http_lua_shdict_list_node_t,\n                                           queue);\n\n                    ngx_slab_free_locked(ctx->shpool, lnode);\n                }\n            }\n\n            ngx_queue_remove(q);\n\n            node = (ngx_rbtree_node_t *)\n                ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));\n\n            ngx_rbtree_delete(&ctx->sh->rbtree, node);\n            ngx_slab_free_locked(ctx->shpool, node);\n            freed++;\n\n            if (attempts && freed == attempts) {\n                break;\n            }\n        }\n\n        q = prev;\n    }\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    lua_pushnumber(L, freed);\n    return 1;\n}\n\n\n/*\n * This trades CPU for memory. This is potentially slow. O(2n)\n */\n\nstatic int\nngx_http_lua_shdict_get_keys(lua_State *L)\n{\n    ngx_queue_t                 *q, *prev;\n    ngx_http_lua_shdict_node_t  *sd;\n    ngx_http_lua_shdict_ctx_t   *ctx;\n    ngx_shm_zone_t              *zone;\n    ngx_time_t                  *tp;\n    int                          total = 0;\n    int                          attempts = 1024;\n    uint64_t                     now;\n    int                          n;\n\n    n = lua_gettop(L);\n\n    if (n != 1 && n != 2) {\n        return luaL_error(L, \"expecting 1 or 2 argument(s), \"\n                          \"but saw %d\", n);\n    }\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    zone = ngx_http_lua_shdict_get_zone(L, 1);\n    if (zone == NULL) {\n        return luaL_error(L, \"bad user data for the ngx_shm_zone_t pointer\");\n    }\n\n    if (n == 2) {\n        attempts = luaL_checkint(L, 2);\n    }\n\n    ctx = zone->data;\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n\n    if (ngx_queue_empty(&ctx->sh->lru_queue)) {\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n        lua_createtable(L, 0, 0);\n        return 1;\n    }\n\n    tp = ngx_timeofday();\n\n    now = (uint64_t) tp->sec * 1000 + tp->msec;\n\n    /* first run through: get total number of elements we need to allocate */\n\n    q = ngx_queue_last(&ctx->sh->lru_queue);\n\n    while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) {\n        prev = ngx_queue_prev(q);\n\n        sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);\n\n        if (sd->expires == 0 || sd->expires > now) {\n            total++;\n            if (attempts && total == attempts) {\n                break;\n            }\n        }\n\n        q = prev;\n    }\n\n    lua_createtable(L, total, 0);\n\n    /* second run through: add keys to table */\n\n    total = 0;\n    q = ngx_queue_last(&ctx->sh->lru_queue);\n\n    while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) {\n        prev = ngx_queue_prev(q);\n\n        sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);\n\n        if (sd->expires == 0 || sd->expires > now) {\n            lua_pushlstring(L, (char *) sd->data, sd->key_len);\n            lua_rawseti(L, -2, ++total);\n            if (attempts && total == attempts) {\n                break;\n            }\n        }\n\n        q = prev;\n    }\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    /* table is at top of stack */\n    return 1;\n}\n\n\nngx_int_t\nngx_http_lua_shared_dict_get(ngx_shm_zone_t *zone, u_char *key_data,\n    size_t key_len, ngx_http_lua_value_t *value)\n{\n    u_char                      *data;\n    size_t                       len;\n    uint32_t                     hash;\n    ngx_int_t                    rc;\n    ngx_http_lua_shdict_ctx_t   *ctx;\n    ngx_http_lua_shdict_node_t  *sd;\n\n    if (zone == NULL) {\n        return NGX_ERROR;\n    }\n\n    hash = ngx_crc32_short(key_data, key_len);\n\n    ctx = zone->data;\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n\n    rc = ngx_http_lua_shdict_lookup(zone, hash, key_data, key_len, &sd);\n\n    dd(\"shdict lookup returned %d\", (int) rc);\n\n    if (rc == NGX_DECLINED || rc == NGX_DONE) {\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        return rc;\n    }\n\n    /* rc == NGX_OK */\n\n    value->type = sd->value_type;\n\n    dd(\"type: %d\", (int) value->type);\n\n    data = sd->data + sd->key_len;\n    len = (size_t) sd->value_len;\n\n    switch (value->type) {\n\n    case SHDICT_TSTRING:\n\n        if (value->value.s.data == NULL || value->value.s.len == 0) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"no string buffer \"\n                          \"initialized\");\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n            return NGX_ERROR;\n        }\n\n        if (len > value->value.s.len) {\n            len = value->value.s.len;\n\n        } else {\n            value->value.s.len = len;\n        }\n\n        ngx_memcpy(value->value.s.data, data, len);\n        break;\n\n    case SHDICT_TNUMBER:\n\n        if (len != sizeof(double)) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"bad lua number \"\n                          \"value size found for key %*s: %lu\", key_len,\n                          key_data, (unsigned long) len);\n\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(&value->value.n, data, len);\n        break;\n\n    case SHDICT_TBOOLEAN:\n\n        if (len != sizeof(u_char)) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"bad lua boolean \"\n                          \"value size found for key %*s: %lu\", key_len,\n                          key_data, (unsigned long) len);\n\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n            return NGX_ERROR;\n        }\n\n        value->value.b = *data;\n        break;\n\n    default:\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"bad lua value type \"\n                      \"found for key %*s: %d\", key_len, key_data,\n                      (int) value->type);\n\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n        return NGX_ERROR;\n    }\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n    return NGX_OK;\n}\n\n\nstatic int\nngx_http_lua_shdict_lpush(lua_State *L)\n{\n    return ngx_http_lua_shdict_push_helper(L, NGX_HTTP_LUA_SHDICT_LEFT);\n}\n\n\nstatic int\nngx_http_lua_shdict_rpush(lua_State *L)\n{\n    return ngx_http_lua_shdict_push_helper(L, NGX_HTTP_LUA_SHDICT_RIGHT);\n}\n\n\nstatic int\nngx_http_lua_shdict_push_helper(lua_State *L, int flags)\n{\n    int                              n;\n    ngx_str_t                        key;\n    uint32_t                         hash;\n    ngx_int_t                        rc;\n    ngx_http_lua_shdict_ctx_t       *ctx;\n    ngx_http_lua_shdict_node_t      *sd;\n    ngx_str_t                        value;\n    int                              value_type;\n    double                           num;\n    ngx_rbtree_node_t               *node;\n    ngx_shm_zone_t                  *zone;\n    ngx_queue_t                     *queue, *q;\n    ngx_http_lua_shdict_list_node_t *lnode;\n\n    n = lua_gettop(L);\n\n    if (n != 3) {\n        return luaL_error(L, \"expecting 3 arguments, \"\n                          \"but only seen %d\", n);\n    }\n\n    if (lua_type(L, 1) != LUA_TTABLE) {\n        return luaL_error(L, \"bad \\\"zone\\\" argument\");\n    }\n\n    zone = ngx_http_lua_shdict_get_zone(L, 1);\n    if (zone == NULL) {\n        return luaL_error(L, \"bad \\\"zone\\\" argument\");\n    }\n\n    ctx = zone->data;\n\n    if (lua_isnil(L, 2)) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"nil key\");\n        return 2;\n    }\n\n    key.data = (u_char *) luaL_checklstring(L, 2, &key.len);\n\n    if (key.len == 0) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"empty key\");\n        return 2;\n    }\n\n    if (key.len > 65535) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"key too long\");\n        return 2;\n    }\n\n    hash = ngx_crc32_short(key.data, key.len);\n\n    value_type = lua_type(L, 3);\n\n    switch (value_type) {\n\n    case SHDICT_TSTRING:\n        value.data = (u_char *) lua_tolstring(L, 3, &value.len);\n        break;\n\n    case SHDICT_TNUMBER:\n        value.len = sizeof(double);\n        num = lua_tonumber(L, 3);\n        value.data = (u_char *) &num;\n        break;\n\n    default:\n        lua_pushnil(L);\n        lua_pushliteral(L, \"bad value type\");\n        return 2;\n    }\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n\n#if 1\n    ngx_http_lua_shdict_expire(ctx, 1);\n#endif\n\n    rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);\n\n    dd(\"shdict lookup returned %d\", (int) rc);\n\n    /* exists but expired */\n\n    if (rc == NGX_DONE) {\n\n        if (sd->value_type != SHDICT_TLIST) {\n            /* TODO: reuse when length matched */\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                           \"lua shared dict push: found old entry and value \"\n                           \"type not matched, remove it first\");\n\n            ngx_queue_remove(&sd->queue);\n\n            node = (ngx_rbtree_node_t *)\n                        ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));\n\n            ngx_rbtree_delete(&ctx->sh->rbtree, node);\n\n            ngx_slab_free_locked(ctx->shpool, node);\n\n            dd(\"go to init_list\");\n            goto init_list;\n        }\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                       \"lua shared dict push: found old entry and value \"\n                       \"type matched, reusing it\");\n\n        sd->expires = 0;\n        sd->value_len = 0;\n        /* free list nodes */\n\n        queue = ngx_http_lua_shdict_get_list_head(sd, key.len);\n\n        for (q = ngx_queue_head(queue);\n             q != ngx_queue_sentinel(queue);\n             q = ngx_queue_next(q))\n        {\n            /* TODO: reuse matched size list node */\n            lnode = ngx_queue_data(q, ngx_http_lua_shdict_list_node_t, queue);\n            ngx_slab_free_locked(ctx->shpool, lnode);\n        }\n\n        ngx_queue_init(queue);\n\n        ngx_queue_remove(&sd->queue);\n        ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);\n\n        dd(\"go to push_node\");\n        goto push_node;\n    }\n\n    /* exists and not expired */\n\n    if (rc == NGX_OK) {\n\n        if (sd->value_type != SHDICT_TLIST) {\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n            lua_pushnil(L);\n            lua_pushliteral(L, \"value not a list\");\n            return 2;\n        }\n\n        queue = ngx_http_lua_shdict_get_list_head(sd, key.len);\n\n        ngx_queue_remove(&sd->queue);\n        ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);\n\n        dd(\"go to push_node\");\n        goto push_node;\n    }\n\n    /* rc == NGX_DECLINED, not found */\n\ninit_list:\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"lua shared dict list: creating a new entry\");\n\n    /* NOTICE: we assume the begin point aligned in slab, be careful */\n    n = offsetof(ngx_rbtree_node_t, color)\n        + offsetof(ngx_http_lua_shdict_node_t, data)\n        + key.len\n        + sizeof(ngx_queue_t);\n\n    dd(\"length before aligned: %d\", n);\n\n    n = (int) (uintptr_t) ngx_align_ptr(n, NGX_ALIGNMENT);\n\n    dd(\"length after aligned: %d\", n);\n\n    node = ngx_slab_alloc_locked(ctx->shpool, n);\n\n    if (node == NULL) {\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        lua_pushboolean(L, 0);\n        lua_pushliteral(L, \"no memory\");\n        return 2;\n    }\n\n    sd = (ngx_http_lua_shdict_node_t *) &node->color;\n\n    queue = ngx_http_lua_shdict_get_list_head(sd, key.len);\n\n    node->key = hash;\n    sd->key_len = (u_short) key.len;\n\n    sd->expires = 0;\n\n    sd->value_len = 0;\n\n    dd(\"setting value type to %d\", (int) SHDICT_TLIST);\n\n    sd->value_type = (uint8_t) SHDICT_TLIST;\n\n    ngx_memcpy(sd->data, key.data, key.len);\n\n    ngx_queue_init(queue);\n\n    ngx_rbtree_insert(&ctx->sh->rbtree, node);\n\n    ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);\n\npush_node:\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"lua shared dict list: creating a new list node\");\n\n    n = offsetof(ngx_http_lua_shdict_list_node_t, data)\n        + value.len;\n\n    dd(\"list node length: %d\", n);\n\n    lnode = ngx_slab_alloc_locked(ctx->shpool, n);\n\n    if (lnode == NULL) {\n\n        if (sd->value_len == 0) {\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                           \"lua shared dict list: no memory for create\"\n                           \" list node and list empty, remove it\");\n\n            ngx_queue_remove(&sd->queue);\n\n            node = (ngx_rbtree_node_t *)\n                        ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));\n\n            ngx_rbtree_delete(&ctx->sh->rbtree, node);\n\n            ngx_slab_free_locked(ctx->shpool, node);\n        }\n\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        lua_pushnil(L);\n        lua_pushliteral(L, \"no memory\");\n        return 2;\n    }\n\n    dd(\"setting list length to %d\", sd->value_len + 1);\n\n    sd->value_len = sd->value_len + 1;\n\n    dd(\"setting list node value length to %d\", (int) value.len);\n\n    lnode->value_len = (uint32_t) value.len;\n\n    dd(\"setting list node value type to %d\", value_type);\n\n    lnode->value_type = (uint8_t) value_type;\n\n    ngx_memcpy(lnode->data, value.data, value.len);\n\n    if (flags == NGX_HTTP_LUA_SHDICT_LEFT) {\n        ngx_queue_insert_head(queue, &lnode->queue);\n\n    } else {\n        ngx_queue_insert_tail(queue, &lnode->queue);\n    }\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    lua_pushnumber(L, sd->value_len);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_shdict_lpop(lua_State *L)\n{\n    return ngx_http_lua_shdict_pop_helper(L, NGX_HTTP_LUA_SHDICT_LEFT);\n}\n\n\nstatic int\nngx_http_lua_shdict_rpop(lua_State *L)\n{\n    return ngx_http_lua_shdict_pop_helper(L, NGX_HTTP_LUA_SHDICT_RIGHT);\n}\n\n\nstatic int\nngx_http_lua_shdict_pop_helper(lua_State *L, int flags)\n{\n    int                              n;\n    ngx_str_t                        name;\n    ngx_str_t                        key;\n    uint32_t                         hash;\n    ngx_int_t                        rc;\n    ngx_http_lua_shdict_ctx_t       *ctx;\n    ngx_http_lua_shdict_node_t      *sd;\n    ngx_str_t                        value;\n    int                              value_type;\n    double                           num;\n    ngx_rbtree_node_t               *node;\n    ngx_shm_zone_t                  *zone;\n    ngx_queue_t                     *queue;\n    ngx_http_lua_shdict_list_node_t *lnode;\n\n    n = lua_gettop(L);\n\n    if (n != 2) {\n        return luaL_error(L, \"expecting 2 arguments, \"\n                          \"but only seen %d\", n);\n    }\n\n    if (lua_type(L, 1) != LUA_TTABLE) {\n        return luaL_error(L, \"bad \\\"zone\\\" argument\");\n    }\n\n    zone = ngx_http_lua_shdict_get_zone(L, 1);\n    if (zone == NULL) {\n        return luaL_error(L, \"bad \\\"zone\\\" argument\");\n    }\n\n    ctx = zone->data;\n    name = ctx->name;\n\n    if (lua_isnil(L, 2)) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"nil key\");\n        return 2;\n    }\n\n    key.data = (u_char *) luaL_checklstring(L, 2, &key.len);\n\n    if (key.len == 0) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"empty key\");\n        return 2;\n    }\n\n    if (key.len > 65535) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"key too long\");\n        return 2;\n    }\n\n    hash = ngx_crc32_short(key.data, key.len);\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n\n#if 1\n    ngx_http_lua_shdict_expire(ctx, 1);\n#endif\n\n    rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);\n\n    dd(\"shdict lookup returned %d\", (int) rc);\n\n    if (rc == NGX_DECLINED || rc == NGX_DONE) {\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n        lua_pushnil(L);\n        return 1;\n    }\n\n    /* rc == NGX_OK */\n\n    if (sd->value_type != SHDICT_TLIST) {\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        lua_pushnil(L);\n        lua_pushliteral(L, \"value not a list\");\n        return 2;\n    }\n\n    if (sd->value_len <= 0) {\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        return luaL_error(L, \"bad lua list length found for key %s \"\n                          \"in shared_dict %s: %lu\", key.data, name.data,\n                          (unsigned long) sd->value_len);\n    }\n\n    queue = ngx_http_lua_shdict_get_list_head(sd, key.len);\n\n    if (flags == NGX_HTTP_LUA_SHDICT_LEFT) {\n        queue = ngx_queue_head(queue);\n\n    } else {\n        queue = ngx_queue_last(queue);\n    }\n\n    lnode = ngx_queue_data(queue, ngx_http_lua_shdict_list_node_t, queue);\n\n    value_type = lnode->value_type;\n\n    dd(\"data: %p\", lnode->data);\n    dd(\"value len: %d\", (int) sd->value_len);\n\n    value.data = lnode->data;\n    value.len = (size_t) lnode->value_len;\n\n    switch (value_type) {\n\n    case SHDICT_TSTRING:\n\n        lua_pushlstring(L, (char *) value.data, value.len);\n        break;\n\n    case SHDICT_TNUMBER:\n\n        if (value.len != sizeof(double)) {\n\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n            return luaL_error(L, \"bad lua list node number value size found \"\n                              \"for key %s in shared_dict %s: %lu\", key.data,\n                              name.data, (unsigned long) value.len);\n        }\n\n        ngx_memcpy(&num, value.data, sizeof(double));\n\n        lua_pushnumber(L, num);\n        break;\n\n    default:\n\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        return luaL_error(L, \"bad list node value type found for key %s in \"\n                          \"shared_dict %s: %d\", key.data, name.data,\n                          value_type);\n    }\n\n    ngx_queue_remove(queue);\n\n    ngx_slab_free_locked(ctx->shpool, lnode);\n\n    if (sd->value_len == 1) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                       \"lua shared dict list: empty node after pop, \"\n                       \"remove it\");\n\n        ngx_queue_remove(&sd->queue);\n\n        node = (ngx_rbtree_node_t *)\n                    ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));\n\n        ngx_rbtree_delete(&ctx->sh->rbtree, node);\n\n        ngx_slab_free_locked(ctx->shpool, node);\n\n    } else {\n        sd->value_len = sd->value_len - 1;\n\n        ngx_queue_remove(&sd->queue);\n        ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);\n    }\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_shdict_llen(lua_State *L)\n{\n    int                          n;\n    ngx_str_t                    key;\n    uint32_t                     hash;\n    ngx_int_t                    rc;\n    ngx_http_lua_shdict_ctx_t   *ctx;\n    ngx_http_lua_shdict_node_t  *sd;\n    ngx_shm_zone_t              *zone;\n\n    n = lua_gettop(L);\n\n    if (n != 2) {\n        return luaL_error(L, \"expecting 2 arguments, \"\n                          \"but only seen %d\", n);\n    }\n\n    if (lua_type(L, 1) != LUA_TTABLE) {\n        return luaL_error(L, \"bad \\\"zone\\\" argument\");\n    }\n\n    zone = ngx_http_lua_shdict_get_zone(L, 1);\n    if (zone == NULL) {\n        return luaL_error(L, \"bad \\\"zone\\\" argument\");\n    }\n\n    ctx = zone->data;\n\n    if (lua_isnil(L, 2)) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"nil key\");\n        return 2;\n    }\n\n    key.data = (u_char *) luaL_checklstring(L, 2, &key.len);\n\n    if (key.len == 0) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"empty key\");\n        return 2;\n    }\n\n    if (key.len > 65535) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"key too long\");\n        return 2;\n    }\n\n    hash = ngx_crc32_short(key.data, key.len);\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n\n#if 1\n    ngx_http_lua_shdict_expire(ctx, 1);\n#endif\n\n    rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);\n\n    dd(\"shdict lookup returned %d\", (int) rc);\n\n    if (rc == NGX_OK) {\n\n        if (sd->value_type != SHDICT_TLIST) {\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n            lua_pushnil(L);\n            lua_pushliteral(L, \"value not a list\");\n            return 2;\n        }\n\n        ngx_queue_remove(&sd->queue);\n        ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);\n\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        lua_pushnumber(L, (lua_Number) sd->value_len);\n        return 1;\n    }\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    lua_pushnumber(L, 0);\n    return 1;\n}\n\n\nngx_shm_zone_t *\nngx_http_lua_find_zone(u_char *name_data, size_t name_len)\n{\n    ngx_str_t                       *name;\n    ngx_uint_t                       i;\n    ngx_shm_zone_t                  *zone;\n    ngx_http_lua_shm_zone_ctx_t     *ctx;\n    volatile ngx_list_part_t        *part;\n\n    part = &ngx_cycle->shared_memory.part;\n    zone = part->elts;\n\n    for (i = 0; /* void */ ; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n\n            part = part->next;\n            zone = part->elts;\n            i = 0;\n        }\n\n        name = &zone[i].shm.name;\n\n        dd(\"name: [%.*s] %d\", (int) name->len, name->data, (int) name->len);\n        dd(\"name2: [%.*s] %d\", (int) name_len, name_data, (int) name_len);\n\n        if (name->len == name_len\n            && ngx_strncmp(name->data, name_data, name_len) == 0)\n        {\n            ctx = (ngx_http_lua_shm_zone_ctx_t *) zone[i].data;\n            return &ctx->zone;\n        }\n    }\n\n    return NULL;\n}\n\n\nngx_shm_zone_t *\nngx_http_lua_ffi_shdict_udata_to_zone(void *zone_udata)\n{\n    if (zone_udata == NULL) {\n        return NULL;\n    }\n\n    return *(ngx_shm_zone_t **) zone_udata;\n}\n\n\nint\nngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key,\n    size_t key_len, int value_type, u_char *str_value_buf,\n    size_t str_value_len, double num_value, long exptime, int user_flags,\n    char **errmsg, int *forcible)\n{\n    int                          i, n;\n    u_char                       c, *p;\n    uint32_t                     hash;\n    ngx_int_t                    rc;\n    ngx_time_t                  *tp;\n    ngx_queue_t                 *queue, *q;\n    ngx_rbtree_node_t           *node;\n    ngx_http_lua_shdict_ctx_t   *ctx;\n    ngx_http_lua_shdict_node_t  *sd;\n\n    dd(\"exptime: %ld\", exptime);\n\n    ctx = zone->data;\n\n    *forcible = 0;\n\n    hash = ngx_crc32_short(key, key_len);\n\n    switch (value_type) {\n\n    case SHDICT_TSTRING:\n        /* do nothing */\n        break;\n\n    case SHDICT_TNUMBER:\n        dd(\"num value: %lf\", num_value);\n        str_value_buf = (u_char *) &num_value;\n        str_value_len = sizeof(double);\n        break;\n\n    case SHDICT_TBOOLEAN:\n        c = num_value ? 1 : 0;\n        str_value_buf = &c;\n        str_value_len = sizeof(u_char);\n        break;\n\n    case LUA_TNIL:\n        if (op & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) {\n            *errmsg = \"attempt to add or replace nil values\";\n            return NGX_ERROR;\n        }\n\n        str_value_buf = NULL;\n        str_value_len = 0;\n        break;\n\n    default:\n        *errmsg = \"unsupported value type\";\n        return NGX_ERROR;\n    }\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n\n#if 1\n    ngx_http_lua_shdict_expire(ctx, 1);\n#endif\n\n    rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd);\n\n    dd(\"lookup returns %d\", (int) rc);\n\n    if (op & NGX_HTTP_LUA_SHDICT_REPLACE) {\n\n        if (rc == NGX_DECLINED || rc == NGX_DONE) {\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n            *errmsg = \"not found\";\n            return NGX_DECLINED;\n        }\n\n        /* rc == NGX_OK */\n\n        goto replace;\n    }\n\n    if (op & NGX_HTTP_LUA_SHDICT_ADD) {\n\n        if (rc == NGX_OK) {\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n            *errmsg = \"exists\";\n            return NGX_DECLINED;\n        }\n\n        if (rc == NGX_DONE) {\n            /* exists but expired */\n\n            dd(\"go to replace\");\n            goto replace;\n        }\n\n        /* rc == NGX_DECLINED */\n\n        dd(\"go to insert\");\n        goto insert;\n    }\n\n    if (rc == NGX_OK || rc == NGX_DONE) {\n\n        if (value_type == LUA_TNIL) {\n            goto remove;\n        }\n\nreplace:\n\n        if (str_value_buf\n            && str_value_len == (size_t) sd->value_len\n            && sd->value_type != SHDICT_TLIST)\n        {\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                           \"lua shared dict set: found old entry and value \"\n                           \"size matched, reusing it\");\n\n            ngx_queue_remove(&sd->queue);\n            ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);\n\n            if (exptime > 0) {\n                tp = ngx_timeofday();\n                sd->expires = (uint64_t) tp->sec * 1000 + tp->msec\n                              + (uint64_t) exptime;\n\n            } else {\n                sd->expires = 0;\n            }\n\n            sd->user_flags = user_flags;\n\n            dd(\"setting value type to %d\", value_type);\n\n            sd->value_type = (uint8_t) value_type;\n\n            ngx_memcpy(sd->data + key_len, str_value_buf, str_value_len);\n\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n            return NGX_OK;\n        }\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                       \"lua shared dict set: found old entry but value size \"\n                       \"NOT matched, removing it first\");\n\nremove:\n\n        if (sd->value_type == SHDICT_TLIST) {\n            queue = ngx_http_lua_shdict_get_list_head(sd, key_len);\n\n            for (q = ngx_queue_head(queue);\n                 q != ngx_queue_sentinel(queue);\n                 q = ngx_queue_next(q))\n            {\n                p = (u_char *) ngx_queue_data(q,\n                                              ngx_http_lua_shdict_list_node_t,\n                                              queue);\n\n                ngx_slab_free_locked(ctx->shpool, p);\n            }\n        }\n\n        ngx_queue_remove(&sd->queue);\n\n        node = (ngx_rbtree_node_t *)\n                   ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));\n\n        ngx_rbtree_delete(&ctx->sh->rbtree, node);\n\n        ngx_slab_free_locked(ctx->shpool, node);\n\n    }\n\ninsert:\n\n    /* rc == NGX_DECLINED or value size unmatch */\n\n    if (str_value_buf == NULL) {\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n        return NGX_OK;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"lua shared dict set: creating a new entry\");\n\n    n = offsetof(ngx_rbtree_node_t, color)\n        + offsetof(ngx_http_lua_shdict_node_t, data)\n        + key_len\n        + str_value_len;\n\n    node = ngx_slab_alloc_locked(ctx->shpool, n);\n\n    if (node == NULL) {\n\n        if (op & NGX_HTTP_LUA_SHDICT_SAFE_STORE) {\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n            *errmsg = \"no memory\";\n            return NGX_ERROR;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                       \"lua shared dict set: overriding non-expired items \"\n                       \"due to memory shortage for entry \\\"%*s\\\"\", key_len,\n                       key);\n\n        for (i = 0; i < 30; i++) {\n            if (ngx_http_lua_shdict_expire(ctx, 0) == 0) {\n                break;\n            }\n\n            *forcible = 1;\n\n            node = ngx_slab_alloc_locked(ctx->shpool, n);\n            if (node != NULL) {\n                goto allocated;\n            }\n        }\n\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        *errmsg = \"no memory\";\n        return NGX_ERROR;\n    }\n\nallocated:\n\n    sd = (ngx_http_lua_shdict_node_t *) &node->color;\n\n    node->key = hash;\n    sd->key_len = (u_short) key_len;\n\n    if (exptime > 0) {\n        tp = ngx_timeofday();\n        sd->expires = (uint64_t) tp->sec * 1000 + tp->msec\n                      + (uint64_t) exptime;\n\n    } else {\n        sd->expires = 0;\n    }\n\n    sd->user_flags = user_flags;\n    sd->value_len = (uint32_t) str_value_len;\n    dd(\"setting value type to %d\", value_type);\n    sd->value_type = (uint8_t) value_type;\n\n    p = ngx_copy(sd->data, key, key_len);\n    ngx_memcpy(p, str_value_buf, str_value_len);\n\n    ngx_rbtree_insert(&ctx->sh->rbtree, node);\n    ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_shdict_get(ngx_shm_zone_t *zone, u_char *key,\n    size_t key_len, int *value_type, u_char **str_value_buf,\n    size_t *str_value_len, double *num_value, int *user_flags,\n    int get_stale, int *is_stale, char **err)\n{\n    ngx_str_t                    name;\n    uint32_t                     hash;\n    ngx_int_t                    rc;\n    ngx_http_lua_shdict_ctx_t   *ctx;\n    ngx_http_lua_shdict_node_t  *sd;\n    ngx_str_t                    value;\n\n    *err = NULL;\n\n    ctx = zone->data;\n    name = ctx->name;\n\n    hash = ngx_crc32_short(key, key_len);\n\n#if (NGX_DEBUG)\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"fetching key \\\"%*s\\\" in shared dict \\\"%V\\\"\", key_len,\n                   key, &name);\n#endif /* NGX_DEBUG */\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n\n#if 1\n    if (!get_stale) {\n        ngx_http_lua_shdict_expire(ctx, 1);\n    }\n#endif\n\n    rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd);\n\n    dd(\"shdict lookup returns %d\", (int) rc);\n\n    if (rc == NGX_DECLINED || (rc == NGX_DONE && !get_stale)) {\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n        *value_type = LUA_TNIL;\n        return NGX_OK;\n    }\n\n    /* rc == NGX_OK || (rc == NGX_DONE && get_stale) */\n\n    *value_type = sd->value_type;\n\n    dd(\"data: %p\", sd->data);\n    dd(\"key len: %d\", (int) sd->key_len);\n\n    value.data = sd->data + sd->key_len;\n    value.len = (size_t) sd->value_len;\n\n    if (*str_value_len < (size_t) value.len) {\n        if (*value_type == SHDICT_TBOOLEAN) {\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n            return NGX_ERROR;\n        }\n\n        if (*value_type == SHDICT_TSTRING) {\n            *str_value_buf = malloc(value.len);\n            if (*str_value_buf == NULL) {\n                ngx_shmtx_unlock(&ctx->shpool->mutex);\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    switch (*value_type) {\n\n    case SHDICT_TSTRING:\n        *str_value_len = value.len;\n        ngx_memcpy(*str_value_buf, value.data, value.len);\n        break;\n\n    case SHDICT_TNUMBER:\n\n        if (value.len != sizeof(double)) {\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                          \"bad lua number value size found for key %*s \"\n                          \"in shared_dict %V: %z\", key_len, key,\n                          &name, value.len);\n            return NGX_ERROR;\n        }\n\n        *str_value_len = value.len;\n        ngx_memcpy(num_value, value.data, sizeof(double));\n        break;\n\n    case SHDICT_TBOOLEAN:\n\n        if (value.len != sizeof(u_char)) {\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                          \"bad lua boolean value size found for key %*s \"\n                          \"in shared_dict %V: %z\", key_len, key, &name,\n                          value.len);\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(*str_value_buf, value.data, value.len);\n        break;\n\n    case SHDICT_TLIST:\n\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        *err = \"value is a list\";\n        return NGX_ERROR;\n\n    default:\n\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"bad value type found for key %*s in \"\n                      \"shared_dict %V: %d\", key_len, key, &name,\n                      *value_type);\n        return NGX_ERROR;\n    }\n\n    *user_flags = sd->user_flags;\n    dd(\"user flags: %d\", *user_flags);\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    if (get_stale) {\n\n        /* always return value, flags, stale */\n\n        *is_stale = (rc == NGX_DONE);\n        return NGX_OK;\n    }\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_shdict_incr(ngx_shm_zone_t *zone, u_char *key,\n    size_t key_len, double *value, char **err, int has_init, double init,\n    long init_ttl, int *forcible)\n{\n    int                          i, n;\n    uint32_t                     hash;\n    ngx_int_t                    rc;\n    ngx_time_t                  *tp = NULL;\n    ngx_http_lua_shdict_ctx_t   *ctx;\n    ngx_http_lua_shdict_node_t  *sd;\n    double                       num;\n    ngx_rbtree_node_t           *node;\n    u_char                      *p;\n    ngx_queue_t                 *queue, *q;\n\n    if (init_ttl > 0) {\n        tp = ngx_timeofday();\n    }\n\n    ctx = zone->data;\n\n    *forcible = 0;\n\n    hash = ngx_crc32_short(key, key_len);\n\n    dd(\"looking up key %.*s in shared dict %.*s\", (int) key_len, key,\n       (int) ctx->name.len, ctx->name.data);\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n#if 1\n    ngx_http_lua_shdict_expire(ctx, 1);\n#endif\n    rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd);\n\n    dd(\"shdict lookup returned %d\", (int) rc);\n\n    if (rc == NGX_DECLINED || rc == NGX_DONE) {\n        if (!has_init) {\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n            *err = \"not found\";\n            return NGX_ERROR;\n        }\n\n        /* add value */\n        num = *value + init;\n\n        if (rc == NGX_DONE) {\n\n            /* found an expired item */\n\n            if ((size_t) sd->value_len == sizeof(double)\n                && sd->value_type != SHDICT_TLIST)\n            {\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                               \"lua shared dict incr: found old entry and \"\n                               \"value size matched, reusing it\");\n\n                ngx_queue_remove(&sd->queue);\n                ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);\n\n                dd(\"go to setvalue\");\n                goto setvalue;\n            }\n\n            dd(\"go to remove\");\n            goto remove;\n        }\n\n        dd(\"go to insert\");\n        goto insert;\n    }\n\n    /* rc == NGX_OK */\n\n    if (sd->value_type != SHDICT_TNUMBER || sd->value_len != sizeof(double)) {\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n        *err = \"not a number\";\n        return NGX_ERROR;\n    }\n\n    ngx_queue_remove(&sd->queue);\n    ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);\n\n    dd(\"setting value type to %d\", (int) sd->value_type);\n\n    p = sd->data + key_len;\n\n    ngx_memcpy(&num, p, sizeof(double));\n    num += *value;\n\n    ngx_memcpy(p, (double *) &num, sizeof(double));\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    *value = num;\n    return NGX_OK;\n\nremove:\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"lua shared dict incr: found old entry but value size \"\n                   \"NOT matched, removing it first\");\n\n    if (sd->value_type == SHDICT_TLIST) {\n        queue = ngx_http_lua_shdict_get_list_head(sd, key_len);\n\n        for (q = ngx_queue_head(queue);\n             q != ngx_queue_sentinel(queue);\n             q = ngx_queue_next(q))\n        {\n            p = (u_char *) ngx_queue_data(q, ngx_http_lua_shdict_list_node_t,\n                                          queue);\n\n            ngx_slab_free_locked(ctx->shpool, p);\n        }\n    }\n\n    ngx_queue_remove(&sd->queue);\n\n    node = (ngx_rbtree_node_t *)\n               ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));\n\n    ngx_rbtree_delete(&ctx->sh->rbtree, node);\n\n    ngx_slab_free_locked(ctx->shpool, node);\n\ninsert:\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"lua shared dict incr: creating a new entry\");\n\n    n = offsetof(ngx_rbtree_node_t, color)\n        + offsetof(ngx_http_lua_shdict_node_t, data)\n        + key_len\n        + sizeof(double);\n\n    node = ngx_slab_alloc_locked(ctx->shpool, n);\n\n    if (node == NULL) {\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                       \"lua shared dict incr: overriding non-expired items \"\n                       \"due to memory shortage for entry \\\"%*s\\\"\", key_len,\n                       key);\n\n        for (i = 0; i < 30; i++) {\n            if (ngx_http_lua_shdict_expire(ctx, 0) == 0) {\n                break;\n            }\n\n            *forcible = 1;\n\n            node = ngx_slab_alloc_locked(ctx->shpool, n);\n            if (node != NULL) {\n                goto allocated;\n            }\n        }\n\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        *err = \"no memory\";\n        return NGX_ERROR;\n    }\n\nallocated:\n\n    sd = (ngx_http_lua_shdict_node_t *) &node->color;\n\n    node->key = hash;\n\n    sd->key_len = (u_short) key_len;\n\n    sd->value_len = (uint32_t) sizeof(double);\n\n    ngx_rbtree_insert(&ctx->sh->rbtree, node);\n\n    ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);\n\nsetvalue:\n\n    sd->user_flags = 0;\n\n    if (init_ttl > 0) {\n        sd->expires = (uint64_t) tp->sec * 1000 + tp->msec\n                      + (uint64_t) init_ttl;\n\n    } else {\n        sd->expires = 0;\n    }\n\n    dd(\"setting value type to %d\", LUA_TNUMBER);\n\n    sd->value_type = (uint8_t) LUA_TNUMBER;\n\n    p = ngx_copy(sd->data, key, key_len);\n    ngx_memcpy(p, (double *) &num, sizeof(double));\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    *value = num;\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_shdict_flush_all(ngx_shm_zone_t *zone)\n{\n    ngx_queue_t                 *q;\n    ngx_http_lua_shdict_node_t  *sd;\n    ngx_http_lua_shdict_ctx_t   *ctx;\n\n    ctx = zone->data;\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n\n    for (q = ngx_queue_head(&ctx->sh->lru_queue);\n         q != ngx_queue_sentinel(&ctx->sh->lru_queue);\n         q = ngx_queue_next(q))\n    {\n        sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue);\n        sd->expires = 1;\n    }\n\n    ngx_http_lua_shdict_expire(ctx, 0);\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_shdict_peek(ngx_shm_zone_t *shm_zone, ngx_uint_t hash,\n    u_char *kdata, size_t klen, ngx_http_lua_shdict_node_t **sdp)\n{\n    ngx_int_t                    rc;\n    ngx_rbtree_node_t           *node, *sentinel;\n    ngx_http_lua_shdict_ctx_t   *ctx;\n    ngx_http_lua_shdict_node_t  *sd;\n\n    ctx = shm_zone->data;\n\n    node = ctx->sh->rbtree.root;\n    sentinel = ctx->sh->rbtree.sentinel;\n\n    while (node != sentinel) {\n\n        if (hash < node->key) {\n            node = node->left;\n            continue;\n        }\n\n        if (hash > node->key) {\n            node = node->right;\n            continue;\n        }\n\n        /* hash == node->key */\n\n        sd = (ngx_http_lua_shdict_node_t *) &node->color;\n\n        rc = ngx_memn2cmp(kdata, sd->data, klen, (size_t) sd->key_len);\n\n        if (rc == 0) {\n            *sdp = sd;\n\n            return NGX_OK;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    *sdp = NULL;\n\n    return NGX_DECLINED;\n}\n\n\nlong\nngx_http_lua_ffi_shdict_get_ttl(ngx_shm_zone_t *zone, u_char *key,\n    size_t key_len)\n{\n    uint32_t                     hash;\n    uint64_t                     now;\n    uint64_t                     expires;\n    ngx_int_t                    rc;\n    ngx_time_t                  *tp;\n    ngx_http_lua_shdict_ctx_t   *ctx;\n    ngx_http_lua_shdict_node_t  *sd;\n\n    ctx = zone->data;\n    hash = ngx_crc32_short(key, key_len);\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n\n    rc = ngx_http_lua_shdict_peek(zone, hash, key, key_len, &sd);\n\n    if (rc == NGX_DECLINED) {\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        return NGX_DECLINED;\n    }\n\n    /* rc == NGX_OK */\n\n    expires = sd->expires;\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    if (expires == 0) {\n        return 0;\n    }\n\n    tp = ngx_timeofday();\n    now = (uint64_t) tp->sec * 1000 + tp->msec;\n\n    return expires - now;\n}\n\n\nint\nngx_http_lua_ffi_shdict_set_expire(ngx_shm_zone_t *zone, u_char *key,\n    size_t key_len, long exptime)\n{\n    uint32_t                     hash;\n    ngx_int_t                    rc;\n    ngx_time_t                  *tp = NULL;\n    ngx_http_lua_shdict_ctx_t   *ctx;\n    ngx_http_lua_shdict_node_t  *sd;\n\n    if (exptime > 0) {\n        tp = ngx_timeofday();\n    }\n\n    ctx = zone->data;\n    hash = ngx_crc32_short(key, key_len);\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n\n    rc = ngx_http_lua_shdict_peek(zone, hash, key, key_len, &sd);\n\n    if (rc == NGX_DECLINED) {\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        return NGX_DECLINED;\n    }\n\n    /* rc == NGX_OK */\n\n    if (exptime > 0) {\n        sd->expires = (uint64_t) tp->sec * 1000 + tp->msec\n                      + (uint64_t) exptime;\n\n    } else {\n        sd->expires = 0;\n    }\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    return NGX_OK;\n}\n\n\nsize_t\nngx_http_lua_ffi_shdict_capacity(ngx_shm_zone_t *zone)\n{\n    return zone->shm.size;\n}\n\n\n#if (nginx_version >= 1011007)\nsize_t\nngx_http_lua_ffi_shdict_free_space(ngx_shm_zone_t *zone)\n{\n    size_t                       bytes;\n    ngx_http_lua_shdict_ctx_t   *ctx;\n\n    ctx = zone->data;\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n    bytes = ctx->shpool->pfree * ngx_pagesize;\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    return bytes;\n}\n#endif\n\n\n#if (NGX_DARWIN)\nint\nngx_http_lua_ffi_shdict_get_macos(ngx_http_lua_shdict_get_params_t *p)\n{\n    return ngx_http_lua_ffi_shdict_get(p->zone,\n                                       (u_char *) p->key, p->key_len,\n                                       p->value_type, p->str_value_buf,\n                                       p->str_value_len, p->num_value,\n                                       p->user_flags, p->get_stale,\n                                       p->is_stale, p->errmsg);\n}\n\n\nint\nngx_http_lua_ffi_shdict_store_macos(ngx_http_lua_shdict_store_params_t *p)\n{\n    return ngx_http_lua_ffi_shdict_store(p->zone, p->op,\n                                         (u_char *) p->key, p->key_len,\n                                         p->value_type,\n                                         (u_char *) p->str_value_buf,\n                                         p->str_value_len, p->num_value,\n                                         p->exptime, p->user_flags,\n                                         p->errmsg, p->forcible);\n}\n\n\nint\nngx_http_lua_ffi_shdict_incr_macos(ngx_http_lua_shdict_incr_params_t *p)\n{\n    return ngx_http_lua_ffi_shdict_incr(p->zone, (u_char *) p->key, p->key_len,\n                                        p->num_value, p->errmsg,\n                                        p->has_init, p->init, p->init_ttl,\n                                        p->forcible);\n}\n#endif\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_shdict.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_SHDICT_H_INCLUDED_\n#define _NGX_HTTP_LUA_SHDICT_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\ntypedef struct {\n    u_char                       color;\n    uint8_t                      value_type;\n    u_short                      key_len;\n    uint32_t                     value_len;\n    uint64_t                     expires;\n    ngx_queue_t                  queue;\n    uint32_t                     user_flags;\n    u_char                       data[1];\n} ngx_http_lua_shdict_node_t;\n\n\ntypedef struct {\n    ngx_queue_t                  queue;\n    uint32_t                     value_len;\n    uint8_t                      value_type;\n    u_char                       data[1];\n} ngx_http_lua_shdict_list_node_t;\n\n\ntypedef struct {\n    ngx_rbtree_t                  rbtree;\n    ngx_rbtree_node_t             sentinel;\n    ngx_queue_t                   lru_queue;\n} ngx_http_lua_shdict_shctx_t;\n\n\ntypedef struct {\n    ngx_http_lua_shdict_shctx_t  *sh;\n    ngx_slab_pool_t              *shpool;\n    ngx_str_t                     name;\n    ngx_http_lua_main_conf_t     *main_conf;\n    ngx_log_t                    *log;\n} ngx_http_lua_shdict_ctx_t;\n\n\ntypedef struct {\n    ngx_log_t                   *log;\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_cycle_t                 *cycle;\n    ngx_shm_zone_t               zone;\n} ngx_http_lua_shm_zone_ctx_t;\n\n\n#if (NGX_DARWIN)\ntypedef struct {\n    void                  *zone;\n    const unsigned char   *key;\n    size_t                 key_len;\n    int                   *value_type;\n    unsigned char        **str_value_buf;\n    size_t                *str_value_len;\n    double                *num_value;\n    int                   *user_flags;\n    int                    get_stale;\n    int                   *is_stale;\n    char                 **errmsg;\n} ngx_http_lua_shdict_get_params_t;\n\n\ntypedef struct {\n    void                  *zone;\n    int                    op;\n    const unsigned char   *key;\n    size_t                 key_len;\n    int                    value_type;\n    const unsigned char   *str_value_buf;\n    size_t                 str_value_len;\n    double                 num_value;\n    long                   exptime;\n    int                    user_flags;\n    char                 **errmsg;\n    int                   *forcible;\n} ngx_http_lua_shdict_store_params_t;\n\n\ntypedef struct {\n    void                  *zone;\n    const unsigned char   *key;\n    size_t                 key_len;\n    double                *num_value;\n    char                 **errmsg;\n    int                    has_init;\n    double                 init;\n    long                   init_ttl;\n    int                   *forcible;\n} ngx_http_lua_shdict_incr_params_t;\n#endif\n\n\nngx_int_t ngx_http_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data);\nvoid ngx_http_lua_shdict_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\nvoid ngx_http_lua_inject_shdict_api(ngx_http_lua_main_conf_t *lmcf,\n    lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_SHDICT_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_sleep.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_sleep.h\"\n#include \"ngx_http_lua_contentby.h\"\n\n\nstatic int ngx_http_lua_ngx_sleep(lua_State *L);\nstatic void ngx_http_lua_sleep_handler(ngx_event_t *ev);\nstatic void ngx_http_lua_sleep_cleanup(void *data);\nstatic ngx_int_t ngx_http_lua_sleep_resume(ngx_http_request_t *r);\n\n\nstatic int\nngx_http_lua_ngx_sleep(lua_State *L)\n{\n    int                          n;\n    ngx_int_t                    delay; /* in msec */\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    n = lua_gettop(L);\n    if (n != 1) {\n        return luaL_error(L, \"attempt to pass %d arguments, but accepted 1\", n);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    delay = (ngx_int_t) (luaL_checknumber(L, 1) * 1000);\n\n    if (delay < 0) {\n        return luaL_error(L, \"invalid sleep duration \\\"%d\\\"\", delay);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    coctx = ctx->cur_co_ctx;\n    if (coctx == NULL) {\n        return luaL_error(L, \"no co ctx found\");\n    }\n\n    ngx_http_lua_cleanup_pending_operation(coctx);\n    coctx->cleanup = ngx_http_lua_sleep_cleanup;\n    coctx->data = r;\n\n    coctx->sleep.handler = ngx_http_lua_sleep_handler;\n    coctx->sleep.data = coctx;\n    coctx->sleep.log = r->connection->log;\n\n    if (delay == 0) {\n#ifdef HAVE_POSTED_DELAYED_EVENTS_PATCH\n        dd(\"posting 0 sec sleep event to head of delayed queue\");\n\n        coctx->sleep.delayed = 1;\n        ngx_post_event(&coctx->sleep, &ngx_posted_delayed_events);\n#else\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, \"ngx.sleep(0)\"\n                      \" called without delayed events patch, this will\"\n                      \" hurt performance\");\n        ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay);\n#endif\n\n    } else {\n        dd(\"adding timer with delay %lu ms, r:%.*s\", (unsigned long) delay,\n           (int) r->uri.len, r->uri.data);\n\n        ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay);\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua ready to sleep for %d ms\", delay);\n\n    return lua_yield(L, 0);\n}\n\n\nvoid\nngx_http_lua_sleep_handler(ngx_event_t *ev)\n{\n    ngx_connection_t        *c;\n    ngx_http_request_t      *r;\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_http_log_ctx_t      *log_ctx;\n    ngx_http_lua_co_ctx_t   *coctx;\n\n    coctx = ev->data;\n\n    r = coctx->data;\n    c = r->connection;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (ctx == NULL) {\n        return;\n    }\n\n    if (c->fd != (ngx_socket_t) -1) {  /* not a fake connection */\n        log_ctx = c->log->data;\n        log_ctx->current_request = r;\n    }\n\n    coctx->cleanup = NULL;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"lua sleep timer expired: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    ctx->cur_co_ctx = coctx;\n\n    if (ctx->entered_content_phase) {\n        (void) ngx_http_lua_sleep_resume(r);\n\n    } else {\n        ctx->resume_handler = ngx_http_lua_sleep_resume;\n        ngx_http_core_run_phases(r);\n    }\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nvoid\nngx_http_lua_inject_sleep_api(lua_State *L)\n{\n    lua_pushcfunction(L, ngx_http_lua_ngx_sleep);\n    lua_setfield(L, -2, \"sleep\");\n}\n\n\nstatic void\nngx_http_lua_sleep_cleanup(void *data)\n{\n    ngx_http_lua_co_ctx_t          *coctx = data;\n\n    if (coctx->sleep.timer_set) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua clean up the timer for pending ngx.sleep\");\n\n        ngx_del_timer(&coctx->sleep);\n    }\n\n#ifdef HAVE_POSTED_DELAYED_EVENTS_PATCH\n#if (nginx_version >= 1007005)\n    if (coctx->sleep.posted) {\n#else\n    if (coctx->sleep.prev) {\n#endif\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua clean up the posted event for pending ngx.sleep\");\n\n        /*\n        * We need the extra parentheses around the argument\n        * of ngx_delete_posted_event() just to work around macro issues in\n        * nginx cores older than 1.7.5 (exclusive).\n        */\n        ngx_delete_posted_event((&coctx->sleep));\n    }\n#endif\n}\n\n\nstatic ngx_int_t\nngx_http_lua_sleep_resume(ngx_http_request_t *r)\n{\n    lua_State                   *vm;\n    ngx_connection_t            *c;\n    ngx_int_t                    rc;\n    ngx_uint_t                   nreqs;\n    ngx_http_lua_ctx_t          *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->resume_handler = ngx_http_lua_wev_handler;\n\n    c = r->connection;\n    vm = ngx_http_lua_get_lua_vm(r, ctx);\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(vm, r, ctx, 0);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run thread returned %d\", rc);\n\n    if (rc == NGX_AGAIN) {\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (ctx->entered_content_phase) {\n        ngx_http_lua_finalize_request(r, rc);\n        return NGX_DONE;\n    }\n\n    return rc;\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_sleep.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_SLEEP_H_INCLUDED_\n#define _NGX_HTTP_LUA_SLEEP_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_sleep_api(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_SLEEP_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_socket_tcp.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_socket_tcp.h\"\n#include \"ngx_http_lua_input_filters.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_uthread.h\"\n#include \"ngx_http_lua_output.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_probe.h\"\n\n\nstatic int ngx_http_lua_socket_tcp(lua_State *L);\nstatic int ngx_http_lua_socket_tcp_bind(lua_State *L);\nstatic int ngx_http_lua_socket_tcp_connect(lua_State *L);\n#if (NGX_HTTP_SSL)\nstatic void ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c);\nstatic int ngx_http_lua_ssl_handshake_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L);\n#endif\nstatic int ngx_http_lua_socket_tcp_receive(lua_State *L);\nstatic int ngx_http_lua_socket_tcp_receiveany(lua_State *L);\nstatic int ngx_http_lua_socket_tcp_send(lua_State *L);\nstatic int ngx_http_lua_socket_tcp_close(lua_State *L);\nstatic int ngx_http_lua_socket_tcp_settimeout(lua_State *L);\nstatic int ngx_http_lua_socket_tcp_settimeouts(lua_State *L);\nstatic void ngx_http_lua_socket_tcp_handler(ngx_event_t *ev);\nstatic ngx_int_t ngx_http_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic void ngx_http_lua_socket_init_peer_connection_addr_text(\n    ngx_peer_connection_t *pc);\nstatic void ngx_http_lua_socket_read_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic void ngx_http_lua_socket_send_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic void ngx_http_lua_socket_connected_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic void ngx_http_lua_socket_tcp_cleanup(void *data);\nstatic void ngx_http_lua_socket_tcp_finalize(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic void ngx_http_lua_socket_tcp_finalize_read_part(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic void ngx_http_lua_socket_tcp_finalize_write_part(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic ngx_int_t ngx_http_lua_socket_send(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic ngx_int_t ngx_http_lua_socket_test_connect(ngx_http_request_t *r,\n    ngx_connection_t *c);\nstatic void ngx_http_lua_socket_handle_conn_error(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type);\nstatic void ngx_http_lua_socket_handle_read_error(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type);\nstatic void ngx_http_lua_socket_handle_write_error(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type);\nstatic void ngx_http_lua_socket_handle_conn_success(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic void ngx_http_lua_socket_handle_read_success(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic void ngx_http_lua_socket_handle_write_success(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic int ngx_http_lua_socket_tcp_send_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L);\nstatic int ngx_http_lua_socket_tcp_conn_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L);\nstatic void ngx_http_lua_socket_dummy_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic int ngx_http_lua_socket_tcp_receive_helper(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L);\nstatic void ngx_http_lua_socket_tcp_read_prepare(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, void *data, lua_State *L);\nstatic ngx_int_t ngx_http_lua_socket_tcp_read(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic int ngx_http_lua_socket_tcp_receive_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L);\nstatic ngx_int_t ngx_http_lua_socket_read_line(void *data, ssize_t bytes);\nstatic void ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx);\nstatic int ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L);\nstatic int ngx_http_lua_socket_conn_error_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L);\nstatic int ngx_http_lua_socket_read_error_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L);\nstatic int ngx_http_lua_socket_write_error_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L);\nstatic ngx_int_t ngx_http_lua_socket_read_all(void *data, ssize_t bytes);\nstatic ngx_int_t ngx_http_lua_socket_read_until(void *data, ssize_t bytes);\nstatic ngx_int_t ngx_http_lua_socket_read_chunk(void *data, ssize_t bytes);\nstatic ngx_int_t ngx_http_lua_socket_read_any(void *data, ssize_t bytes);\nstatic int ngx_http_lua_socket_tcp_receiveuntil(lua_State *L);\nstatic int ngx_http_lua_socket_receiveuntil_iterator(lua_State *L);\nstatic ngx_int_t ngx_http_lua_socket_compile_pattern(u_char *data, size_t len,\n    ngx_http_lua_socket_compiled_pattern_t *cp, ngx_log_t *log);\nstatic int ngx_http_lua_socket_cleanup_compiled_pattern(lua_State *L);\nstatic int ngx_http_lua_req_socket(lua_State *L);\nstatic void ngx_http_lua_req_socket_rev_handler(ngx_http_request_t *r);\nstatic int ngx_http_lua_socket_tcp_getreusedtimes(lua_State *L);\nstatic int ngx_http_lua_socket_tcp_setkeepalive(lua_State *L);\nstatic void ngx_http_lua_socket_tcp_create_socket_pool(lua_State *L,\n    ngx_http_request_t *r, ngx_str_t key, ngx_int_t pool_size,\n    ngx_int_t backlog, ngx_http_lua_socket_pool_t **spool);\nstatic ngx_int_t ngx_http_lua_get_keepalive_peer(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic void ngx_http_lua_socket_keepalive_dummy_handler(ngx_event_t *ev);\nstatic int ngx_http_lua_socket_tcp_connect_helper(lua_State *L,\n    ngx_http_lua_socket_tcp_upstream_t *u, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, u_char *host_ref, size_t host_len, in_port_t port,\n    unsigned resuming);\nstatic void ngx_http_lua_socket_tcp_conn_op_timeout_handler(\n    ngx_event_t *ev);\nstatic int ngx_http_lua_socket_tcp_conn_op_timeout_retval_handler(\n    ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L);\nstatic void ngx_http_lua_socket_tcp_resume_conn_op(\n    ngx_http_lua_socket_pool_t *spool);\nstatic void ngx_http_lua_socket_tcp_conn_op_ctx_cleanup(void *data);\nstatic void ngx_http_lua_socket_tcp_conn_op_resume_handler(ngx_event_t *ev);\nstatic ngx_int_t ngx_http_lua_socket_keepalive_close_handler(ngx_event_t *ev);\nstatic void ngx_http_lua_socket_keepalive_rev_handler(ngx_event_t *ev);\nstatic int ngx_http_lua_socket_tcp_conn_op_resume_retval_handler(\n    ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L);\nstatic int ngx_http_lua_socket_tcp_upstream_destroy(lua_State *L);\nstatic int ngx_http_lua_socket_downstream_destroy(lua_State *L);\nstatic void ngx_http_lua_socket_push_input_data(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, ngx_http_lua_socket_tcp_upstream_t *u,\n    lua_State *L);\nstatic ngx_int_t ngx_http_lua_socket_add_pending_data(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, u_char *pos, size_t len, u_char *pat,\n    int prefix, int old_state);\nstatic ngx_int_t ngx_http_lua_socket_add_input_buffer(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u);\nstatic ngx_int_t ngx_http_lua_socket_insert_buffer(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, u_char *pat, size_t prefix);\nstatic ngx_int_t ngx_http_lua_socket_tcp_conn_op_resume(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_lua_socket_tcp_conn_resume(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_lua_socket_tcp_read_resume(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_lua_socket_tcp_write_resume(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_lua_socket_tcp_resume_helper(ngx_http_request_t *r,\n    int socket_op);\nstatic void ngx_http_lua_tcp_queue_conn_op_cleanup(void *data);\nstatic void ngx_http_lua_tcp_resolve_cleanup(void *data);\nstatic void ngx_http_lua_coctx_cleanup(void *data);\nstatic void ngx_http_lua_socket_free_pool(ngx_log_t *log,\n    ngx_http_lua_socket_pool_t *spool);\nstatic int ngx_http_lua_socket_shutdown_pool(lua_State *L);\nstatic void ngx_http_lua_socket_shutdown_pool_helper(\n    ngx_http_lua_socket_pool_t *spool);\nstatic int ngx_http_lua_socket_prepare_error_retvals(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L, ngx_uint_t ft_type);\nstatic void ngx_http_lua_socket_tcp_close_connection(ngx_connection_t *c);\n\n\nenum {\n    SOCKET_CTX_INDEX = 1,\n    SOCKET_KEY_INDEX = 3,\n    SOCKET_CONNECT_TIMEOUT_INDEX = 2,\n    SOCKET_SEND_TIMEOUT_INDEX = 4,\n    SOCKET_READ_TIMEOUT_INDEX = 5,\n    SOCKET_CLIENT_CERT_INDEX  = 6 ,\n    SOCKET_CLIENT_PKEY_INDEX  = 7 ,\n    SOCKET_BIND_INDEX = 8   /* only in upstream cosocket */\n};\n\n\nenum {\n    SOCKET_OP_CONNECT      = 0x01,\n    SOCKET_OP_READ         = 0x02,\n    SOCKET_OP_WRITE        = 0x04,\n    SOCKET_OP_RESUME_CONN  = 0x08,\n};\n\n\nenum {\n    NGX_HTTP_LUA_SOCKOPT_KEEPALIVE = 1,\n    NGX_HTTP_LUA_SOCKOPT_REUSEADDR,\n    NGX_HTTP_LUA_SOCKOPT_TCP_NODELAY,\n    NGX_HTTP_LUA_SOCKOPT_SNDBUF,\n    NGX_HTTP_LUA_SOCKOPT_RCVBUF,\n    NGX_HTTP_LUA_SOCKOPT_KEEPINTVL,\n    NGX_HTTP_LUA_SOCKOPT_KEEPCNT,\n};\n\n\n#define ngx_http_lua_socket_check_busy_connecting(r, u, L)                   \\\n    if ((u)->conn_waiting) {                                                 \\\n        lua_pushnil(L);                                                      \\\n        lua_pushliteral(L, \"socket busy connecting\");                        \\\n        return 2;                                                            \\\n    }\n\n\n#define ngx_http_lua_socket_check_busy_reading(r, u, L)                      \\\n    if ((u)->read_waiting) {                                                 \\\n        lua_pushnil(L);                                                      \\\n        lua_pushliteral(L, \"socket busy reading\");                           \\\n        return 2;                                                            \\\n    }\n\n\n#define ngx_http_lua_socket_check_busy_writing(r, u, L)                      \\\n    if ((u)->write_waiting) {                                                \\\n        lua_pushnil(L);                                                      \\\n        lua_pushliteral(L, \"socket busy writing\");                           \\\n        return 2;                                                            \\\n    }                                                                        \\\n    if ((u)->raw_downstream                                                  \\\n        && ((r)->connection->buffered & NGX_HTTP_LOWLEVEL_BUFFERED))         \\\n    {                                                                        \\\n        lua_pushnil(L);                                                      \\\n        lua_pushliteral(L, \"socket busy writing\");                           \\\n        return 2;                                                            \\\n    }\n\n\nstatic char ngx_http_lua_req_socket_metatable_key;\nstatic char ngx_http_lua_raw_req_socket_metatable_key;\nstatic char ngx_http_lua_tcp_socket_metatable_key;\nstatic char ngx_http_lua_upstream_udata_metatable_key;\nstatic char ngx_http_lua_downstream_udata_metatable_key;\nstatic char ngx_http_lua_pool_udata_metatable_key;\nstatic char ngx_http_lua_pattern_udata_metatable_key;\n\n\n#define ngx_http_lua_tcp_socket_metatable_literal_key  \"__tcp_cosocket_mt\"\n#define ngx_http_lua_tcp_req_socket_metatable_literal_key                    \\\n    \"__tcp_req_cosocket_mt\"\n#define ngx_http_lua_tcp_raw_req_socket_metatable_literal_key                \\\n    \"__tcp_raw_req_cosocket_mt\"\n\n\nvoid\nngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L)\n{\n    ngx_int_t         rc;\n\n    lua_createtable(L, 0, 4 /* nrec */);    /* ngx.socket */\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp);\n    lua_pushvalue(L, -1);\n    lua_setfield(L, -3, \"tcp\");\n    lua_setfield(L, -2, \"stream\");\n\n    {\n        const char  buf[] = \"local sock = ngx.socket.tcp()\"\n                            \" local ok, err = sock:connect(...)\"\n                            \" if ok then return sock else return nil, err end\";\n\n        rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, \"=ngx.socket.connect\");\n    }\n\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_CRIT, log, 0,\n                      \"failed to load Lua code for ngx.socket.connect(): %i\",\n                      rc);\n\n    } else {\n        lua_setfield(L, -2, \"connect\");\n    }\n\n    lua_setfield(L, -2, \"socket\");\n\n    /* {{{req socket object metatable */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          req_socket_metatable_key));\n    lua_createtable(L, 0 /* narr */, 6 /* nrec */);\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive);\n    lua_setfield(L, -2, \"receive\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveany);\n    lua_setfield(L, -2, \"receiveany\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil);\n    lua_setfield(L, -2, \"receiveuntil\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeout);\n    lua_setfield(L, -2, \"settimeout\"); /* ngx socket mt */\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeouts);\n    lua_setfield(L, -2, \"settimeouts\"); /* ngx socket mt */\n\n    lua_pushvalue(L, -1);\n    lua_setfield(L, -2, \"__index\");\n\n    lua_rawset(L, LUA_REGISTRYINDEX);\n\n    lua_pushliteral(L, ngx_http_lua_tcp_req_socket_metatable_literal_key);\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          req_socket_metatable_key));\n    lua_rawget(L, LUA_REGISTRYINDEX);\n    lua_rawset(L, LUA_REGISTRYINDEX);\n    /* }}} */\n\n    /* {{{raw req socket object metatable */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          raw_req_socket_metatable_key));\n    lua_createtable(L, 0 /* narr */, 7 /* nrec */);\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive);\n    lua_setfield(L, -2, \"receive\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveany);\n    lua_setfield(L, -2, \"receiveany\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil);\n    lua_setfield(L, -2, \"receiveuntil\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_send);\n    lua_setfield(L, -2, \"send\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeout);\n    lua_setfield(L, -2, \"settimeout\"); /* ngx socket mt */\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeouts);\n    lua_setfield(L, -2, \"settimeouts\"); /* ngx socket mt */\n\n    lua_pushvalue(L, -1);\n    lua_setfield(L, -2, \"__index\");\n\n    lua_rawset(L, LUA_REGISTRYINDEX);\n\n    lua_pushliteral(L, ngx_http_lua_tcp_raw_req_socket_metatable_literal_key);\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          raw_req_socket_metatable_key));\n    lua_rawget(L, LUA_REGISTRYINDEX);\n    lua_rawset(L, LUA_REGISTRYINDEX);\n    /* }}} */\n\n    /* {{{tcp object metatable */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          tcp_socket_metatable_key));\n    lua_createtable(L, 0 /* narr */, 16 /* nrec */);\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_bind);\n    lua_setfield(L, -2, \"bind\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_connect);\n    lua_setfield(L, -2, \"connect\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive);\n    lua_setfield(L, -2, \"receive\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveany);\n    lua_setfield(L, -2, \"receiveany\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil);\n    lua_setfield(L, -2, \"receiveuntil\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_send);\n    lua_setfield(L, -2, \"send\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_close);\n    lua_setfield(L, -2, \"close\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeout);\n    lua_setfield(L, -2, \"settimeout\"); /* ngx socket mt */\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeouts);\n    lua_setfield(L, -2, \"settimeouts\"); /* ngx socket mt */\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_getreusedtimes);\n    lua_setfield(L, -2, \"getreusedtimes\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_setkeepalive);\n    lua_setfield(L, -2, \"setkeepalive\");\n\n    lua_pushvalue(L, -1);\n    lua_setfield(L, -2, \"__index\");\n    lua_rawset(L, LUA_REGISTRYINDEX);\n\n    lua_pushliteral(L, ngx_http_lua_tcp_socket_metatable_literal_key);\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          tcp_socket_metatable_key));\n    lua_rawget(L, LUA_REGISTRYINDEX);\n    lua_rawset(L, LUA_REGISTRYINDEX);\n    /* }}} */\n\n    /* {{{upstream userdata metatable */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          upstream_udata_metatable_key));\n    lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */\n    lua_pushcfunction(L, ngx_http_lua_socket_tcp_upstream_destroy);\n    lua_setfield(L, -2, \"__gc\");\n    lua_rawset(L, LUA_REGISTRYINDEX);\n    /* }}} */\n\n    /* {{{downstream userdata metatable */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          downstream_udata_metatable_key));\n    lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */\n    lua_pushcfunction(L, ngx_http_lua_socket_downstream_destroy);\n    lua_setfield(L, -2, \"__gc\");\n    lua_rawset(L, LUA_REGISTRYINDEX);\n    /* }}} */\n\n    /* {{{socket pool userdata metatable */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          pool_udata_metatable_key));\n    lua_createtable(L, 0, 1); /* metatable */\n    lua_pushcfunction(L, ngx_http_lua_socket_shutdown_pool);\n    lua_setfield(L, -2, \"__gc\");\n    lua_rawset(L, LUA_REGISTRYINDEX);\n    /* }}} */\n\n    /* {{{socket compiled pattern userdata metatable */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          pattern_udata_metatable_key));\n    lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */\n    lua_pushcfunction(L, ngx_http_lua_socket_cleanup_compiled_pattern);\n    lua_setfield(L, -2, \"__gc\");\n    lua_rawset(L, LUA_REGISTRYINDEX);\n    /* }}} */\n}\n\n\nvoid\nngx_http_lua_inject_req_socket_api(lua_State *L)\n{\n    lua_pushcfunction(L, ngx_http_lua_req_socket);\n    lua_setfield(L, -2, \"socket\");\n}\n\n\nstatic u_char *\nngx_http_lua_socket_tcp_log_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char                 *p;\n    in_port_t               port;\n    ngx_http_request_t     *r;\n\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n\n    u = log->data;\n    port = ngx_inet_get_port((struct sockaddr *) &u->sockaddr);\n    if (port == 0) {\n        p = ngx_snprintf(buf, len, \", upstream: %s\", u->host);\n\n    } else {\n        p = ngx_snprintf(buf, len, \", upstream: %s:%ud\",\n                         u->host, port);\n    }\n\n    len -= p - buf;\n    if (u->peer.sockaddr != NULL) {\n        int    addr_text_len;\n        u_char addr_text[NGX_UNIX_ADDRSTRLEN];\n\n        buf = p;\n        addr_text_len = ngx_sock_ntop(u->peer.sockaddr, u->peer.socklen,\n                                      addr_text, NGX_UNIX_ADDRSTRLEN, 0);\n        p = ngx_snprintf(buf, len, \"(%*s)\", addr_text_len, addr_text);\n        len -= p - buf;\n    }\n\n    r = u->request;\n    if (r != NULL) {\n        return r->connection->log->handler(r->connection->log, p, len);\n    }\n\n    return p;\n}\n\n\nstatic int\nngx_http_lua_socket_tcp(lua_State *L)\n{\n    ngx_http_request_t      *r;\n    ngx_http_lua_ctx_t      *ctx;\n\n    if (lua_gettop(L) != 0) {\n        return luaL_error(L, \"expecting zero arguments, but got %d\",\n                          lua_gettop(L));\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    lua_createtable(L, 7 /* narr */, 1 /* nrec */);\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          tcp_socket_metatable_key));\n    lua_rawget(L, LUA_REGISTRYINDEX);\n    lua_setmetatable(L, -2);\n\n    dd(\"top: %d\", lua_gettop(L));\n\n    return 1;\n}\n\n\nstatic void\nngx_http_lua_socket_tcp_create_socket_pool(lua_State *L, ngx_http_request_t *r,\n    ngx_str_t key, ngx_int_t pool_size, ngx_int_t backlog,\n    ngx_http_lua_socket_pool_t **spool)\n{\n    u_char                              *p;\n    size_t                               size, key_len;\n    ngx_int_t                            i;\n    ngx_http_lua_socket_pool_t          *sp;\n    ngx_http_lua_socket_pool_item_t     *items;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket connection pool size: %i, backlog: %i\",\n                   pool_size, backlog);\n\n    key_len = ngx_align(key.len + 1, sizeof(void *));\n\n    size = sizeof(ngx_http_lua_socket_pool_t) - 1 + key_len\n           + sizeof(ngx_http_lua_socket_pool_item_t) * pool_size;\n\n    /* before calling this function, the Lua stack is:\n     * -1 key\n     * -2 pools\n     */\n    sp = lua_newuserdata(L, size);\n    if (sp == NULL) {\n        luaL_error(L, \"no memory\");\n        return;\n    }\n\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          pool_udata_metatable_key));\n    lua_rawget(L, LUA_REGISTRYINDEX);\n    lua_setmetatable(L, -2);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket keepalive create connection pool for key\"\n                   \" \\\"%V\\\"\", &key);\n\n    /* a new socket pool with metatable is push to the stack, so now we have:\n     * -1 sp\n     * -2 key\n     * -3 pools\n     *\n     * it is time to set pools[key] to sp.\n     */\n    lua_rawset(L, -3);\n\n    /* clean up the stack for consistency's sake */\n    lua_pop(L, 1);\n\n    sp->backlog = backlog;\n    sp->size = pool_size;\n    sp->connections = 0;\n    sp->lua_vm = ngx_http_lua_get_lua_vm(r, NULL);\n\n    ngx_queue_init(&sp->cache_connect_op);\n    ngx_queue_init(&sp->wait_connect_op);\n    ngx_queue_init(&sp->cache);\n    ngx_queue_init(&sp->free);\n\n    p = ngx_copy(sp->key, key.data, key.len);\n    *p++ = '\\0';\n\n    items = (ngx_http_lua_socket_pool_item_t *) (sp->key + key_len);\n\n    dd(\"items: %p\", items);\n\n    ngx_http_lua_assert((void *) items == ngx_align_ptr(items, sizeof(void *)));\n\n    for (i = 0; i < pool_size; i++) {\n        ngx_queue_insert_head(&sp->free, &items[i].queue);\n        items[i].socket_pool = sp;\n    }\n\n    *spool = sp;\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_connect_helper(lua_State *L,\n    ngx_http_lua_socket_tcp_upstream_t *u, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, u_char *host_ref, size_t host_len, in_port_t port,\n    unsigned resuming)\n{\n    int                                    n;\n    int                                    host_size;\n    int                                    saved_top;\n    ngx_int_t                              rc;\n    ngx_str_t                              host;\n    ngx_str_t                             *conn_op_host;\n    ngx_url_t                              url;\n    ngx_queue_t                           *q;\n    ngx_resolver_ctx_t                    *rctx, temp;\n    ngx_http_lua_co_ctx_t                 *coctx;\n    ngx_http_core_loc_conf_t              *clcf;\n    ngx_http_lua_socket_pool_t            *spool;\n    ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx;\n\n    spool = u->socket_pool;\n    if (spool != NULL) {\n        rc = ngx_http_lua_get_keepalive_peer(r, u);\n\n        if (rc == NGX_OK) {\n            lua_pushinteger(L, 1);\n            return 1;\n        }\n\n        /* rc == NGX_DECLINED */\n\n        spool->connections++;\n\n        /* check if backlog is enabled and\n         * don't queue resuming connection operation */\n        if (spool->backlog >= 0 && !resuming) {\n\n            dd(\"lua tcp socket %s connections %ld\",\n               spool->key, spool->connections);\n\n            if (spool->connections > spool->size + spool->backlog) {\n                spool->connections--;\n                lua_pushnil(L);\n                lua_pushliteral(L, \"too many waiting connect operations\");\n                return 2;\n            }\n\n            if (spool->connections > spool->size) {\n                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, u->peer.log, 0,\n                               \"lua tcp socket queue connect operation for \"\n                               \"connection pool \\\"%s\\\", connections: %i\",\n                               spool->key, spool->connections);\n\n                host_size = sizeof(u_char) *\n                    (ngx_max(host_len, NGX_INET_ADDRSTRLEN) + 1);\n\n                if (!ngx_queue_empty(&spool->cache_connect_op)) {\n                    q = ngx_queue_last(&spool->cache_connect_op);\n                    ngx_queue_remove(q);\n                    conn_op_ctx = ngx_queue_data(\n                        q, ngx_http_lua_socket_tcp_conn_op_ctx_t, queue);\n\n                    conn_op_host = &conn_op_ctx->host;\n                    if (host_len > conn_op_host->len\n                        && host_len > NGX_INET_ADDRSTRLEN)\n                    {\n                        ngx_free(conn_op_host->data);\n                        conn_op_host->data = ngx_alloc(host_size,\n                                                       ngx_cycle->log);\n                        if (conn_op_host->data == NULL) {\n                            ngx_free(conn_op_ctx);\n                            goto no_memory_and_not_resuming;\n                        }\n                    }\n\n                } else {\n                    conn_op_ctx = ngx_alloc(\n                        sizeof(ngx_http_lua_socket_tcp_conn_op_ctx_t),\n                        ngx_cycle->log);\n                    if (conn_op_ctx == NULL) {\n                        goto no_memory_and_not_resuming;\n                    }\n\n                    conn_op_host = &conn_op_ctx->host;\n                    conn_op_host->data = ngx_alloc(host_size, ngx_cycle->log);\n                    if (conn_op_host->data == NULL) {\n                        ngx_free(conn_op_ctx);\n                        goto no_memory_and_not_resuming;\n                    }\n                }\n\n                conn_op_ctx->cleanup = NULL;\n\n                ngx_memcpy(conn_op_host->data, host_ref, host_len);\n                conn_op_host->data[host_len] = '\\0';\n                conn_op_host->len = host_len;\n\n                conn_op_ctx->port = port;\n\n                u->write_co_ctx = ctx->cur_co_ctx;\n\n                conn_op_ctx->u = u;\n                ctx->cur_co_ctx->cleanup =\n                    ngx_http_lua_tcp_queue_conn_op_cleanup;\n                ctx->cur_co_ctx->data = conn_op_ctx;\n\n                ngx_memzero(&conn_op_ctx->event, sizeof(ngx_event_t));\n                conn_op_ctx->event.handler =\n                    ngx_http_lua_socket_tcp_conn_op_timeout_handler;\n                conn_op_ctx->event.data = conn_op_ctx;\n                conn_op_ctx->event.log = ngx_cycle->log;\n\n                ngx_add_timer(&conn_op_ctx->event, u->connect_timeout);\n\n                ngx_queue_insert_tail(&spool->wait_connect_op,\n                                      &conn_op_ctx->queue);\n\n                ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                               \"lua tcp socket queued connect operation for \"\n                               \"%d(ms), u: %p, ctx: %p\",\n                               u->connect_timeout, conn_op_ctx->u, conn_op_ctx);\n\n                return lua_yield(L, 0);\n            }\n        }\n\n    } /* end spool != NULL */\n\n    host.data = ngx_palloc(r->pool, host_len + 1);\n    if (host.data == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    host.len = host_len;\n\n    ngx_memcpy(host.data, host_ref, host_len);\n    host.data[host_len] = '\\0';\n\n    ngx_memzero(&url, sizeof(ngx_url_t));\n    url.url = host;\n    url.default_port = port;\n    url.no_resolve = 1;\n\n    u->log.data = u;\n    u->log.handler = ngx_http_lua_socket_tcp_log_error;\n\n    coctx = ctx->cur_co_ctx;\n\n    if (ngx_parse_url(r->pool, &url) != NGX_OK) {\n        lua_pushnil(L);\n\n        if (url.err) {\n            lua_pushfstring(L, \"failed to parse host name \\\"%s\\\": %s\",\n                            url.url.data, url.err);\n\n        } else {\n            lua_pushfstring(L, \"failed to parse host name \\\"%s\\\"\",\n                            url.url.data);\n        }\n\n        goto failed;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket connect timeout: %M\", u->connect_timeout);\n\n    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));\n    if (u->resolved == NULL) {\n        if (resuming) {\n            lua_pushnil(L);\n            lua_pushliteral(L, \"no memory\");\n            goto failed;\n        }\n\n        goto no_memory_and_not_resuming;\n    }\n\n    if (url.addrs && url.addrs[0].sockaddr) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket network address given directly\");\n\n        u->resolved->sockaddr = url.addrs[0].sockaddr;\n        u->resolved->socklen = url.addrs[0].socklen;\n        u->resolved->naddrs = 1;\n        if (url.family == AF_UNIX) {\n            u->resolved->host = url.addrs[0].name;\n\n        } else {\n            u->resolved->host = url.host;\n        }\n\n    } else {\n        u->resolved->host = url.host;\n        u->resolved->port = url.default_port;\n    }\n\n    if (u->resolved->host.len < sizeof(u->host)) {\n        ngx_memcpy(u->host, u->resolved->host.data, u->resolved->host.len);\n\n    } else {\n        ngx_memcpy(u->host, u->resolved->host.data, sizeof(u->host) - 4);\n        u->host[sizeof(u->host) - 4] = '.';\n        u->host[sizeof(u->host) - 3] = '.';\n        u->host[sizeof(u->host) - 2] = '.';\n        u->host[sizeof(u->host) - 1] = '\\0';\n    }\n\n    if (u->resolved->sockaddr) {\n        rc = ngx_http_lua_socket_resolve_retval_handler(r, u, L);\n        if (rc == NGX_AGAIN && !resuming) {\n            return lua_yield(L, 0);\n        }\n\n        if (rc > 1) {\n            goto failed;\n        }\n\n        return rc;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    temp.name = host;\n    rctx = ngx_resolve_start(clcf->resolver, &temp);\n    if (rctx == NULL) {\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER;\n        lua_pushnil(L);\n        lua_pushliteral(L, \"failed to start the resolver\");\n        goto failed;\n    }\n\n    if (rctx == NGX_NO_RESOLVER) {\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER;\n        lua_pushnil(L);\n        lua_pushfstring(L, \"no resolver defined to resolve \\\"%s\\\"\", host.data);\n        goto failed;\n    }\n\n    rctx->name = host;\n    rctx->handler = ngx_http_lua_socket_resolve_handler;\n    rctx->data = u;\n    rctx->timeout = clcf->resolver_timeout;\n\n    u->resolved->ctx = rctx;\n    u->write_co_ctx = ctx->cur_co_ctx;\n\n    ngx_http_lua_cleanup_pending_operation(coctx);\n    coctx->cleanup = ngx_http_lua_tcp_resolve_cleanup;\n    coctx->data = u;\n\n    saved_top = lua_gettop(L);\n\n    if (ngx_resolve_name(rctx) != NGX_OK) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket fail to run resolver immediately\");\n\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER;\n\n        coctx->cleanup = NULL;\n        coctx->data = NULL;\n\n        u->resolved->ctx = NULL;\n        lua_pushnil(L);\n        lua_pushfstring(L, \"%s could not be resolved\", host.data);\n        goto failed;\n    }\n\n    if (u->conn_waiting) {\n        dd(\"resolved and already connecting\");\n\n        if (resuming) {\n            return NGX_AGAIN;\n        }\n\n        return lua_yield(L, 0);\n    }\n\n    n = lua_gettop(L) - saved_top;\n    if (n) {\n        dd(\"errors occurred during resolving or connecting\"\n           \"or already connected\");\n\n        if (n > 1) {\n            goto failed;\n        }\n\n        return n;\n    }\n\n    /* still resolving */\n\n    u->conn_waiting = 1;\n    u->write_prepare_retvals = ngx_http_lua_socket_resolve_retval_handler;\n\n    dd(\"setting data to %p\", u);\n\n    if (ctx->entered_content_phase) {\n        r->write_event_handler = ngx_http_lua_content_wev_handler;\n\n    } else {\n        r->write_event_handler = ngx_http_core_run_phases;\n    }\n\n    if (resuming) {\n        return NGX_AGAIN;\n    }\n\n    return lua_yield(L, 0);\n\nfailed:\n\n    if (spool != NULL) {\n        spool->connections--;\n        ngx_http_lua_socket_tcp_resume_conn_op(spool);\n    }\n\n    return 2;\n\nno_memory_and_not_resuming:\n\n    if (spool != NULL) {\n        spool->connections--;\n        ngx_http_lua_socket_tcp_resume_conn_op(spool);\n    }\n\n    return luaL_error(L, \"no memory\");\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_bind(lua_State *L)\n{\n    int                   port;\n    ngx_http_request_t   *r;\n    ngx_http_lua_ctx_t   *ctx;\n    int                   n;\n    u_char               *text;\n    size_t                len;\n    ngx_addr_t           *local;\n\n    n = lua_gettop(L);\n\n    /* Correct the parameter check and allow 2 or 3 parameters */\n    if (n != 2 && n != 3) {\n        return luaL_error(L, \"expecting 2 or 3 arguments, but got %d\",\n                          lua_gettop(L));\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    port = 0;\n    /* handle case: host:port */\n    /* Hit the following parameter combination:\n     * sock:bind(\"127.0.0.1\", port)     */\n    if (n == 3) {\n        if (!lua_isnumber(L, 3)) {\n            lua_pushnil(L);\n            lua_pushfstring(L, \"expecting port to be a\"\n                            \"number but got type: %s\", luaL_typename(L, 3));\n            return 2;\n        }\n\n        port = (int) lua_tointeger(L, 3);\n        if (port < 0 || port > 65535) {\n            lua_pushnil(L);\n            lua_pushfstring(L, \"bad port number: %d\", port);\n            return 2;\n        }\n    }\n\n    text = (u_char *) luaL_checklstring(L, 2, &len);\n\n    local = ngx_http_lua_parse_addr(L, text, len);\n    if (local == NULL) {\n        lua_pushnil(L);\n        lua_pushfstring(L, \"bad address\");\n        return 2;\n    }\n\n    if (port > 0) {\n        ngx_inet_set_port(local->sockaddr, (in_port_t) port);\n    }\n    /* TODO: we may reuse the userdata here */\n    lua_rawseti(L, 1, SOCKET_BIND_INDEX);\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket bind ip: %V\", &local->name);\n\n    lua_pushboolean(L, 1);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_connect(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    int                          port;\n    int                          n;\n    u_char                      *p;\n    size_t                       len;\n    ngx_http_lua_loc_conf_t     *llcf;\n    ngx_peer_connection_t       *pc;\n    ngx_addr_t                  *local;\n    int                          connect_timeout, send_timeout, read_timeout;\n    unsigned                     custom_pool;\n    int                          key_index;\n    ngx_int_t                    backlog;\n    ngx_int_t                    pool_size;\n    ngx_str_t                    key;\n    const char                  *msg;\n\n    ngx_http_lua_socket_tcp_upstream_t      *u;\n\n    ngx_http_lua_socket_pool_t              *spool;\n\n    n = lua_gettop(L);\n    if (n != 2 && n != 3 && n != 4) {\n        return luaL_error(L, \"ngx.socket connect: expecting 2, 3, or 4 \"\n                          \"arguments (including the object), but seen %d\", n);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    p = (u_char *) luaL_checklstring(L, 2, &len);\n\n    backlog = -1;\n    key_index = 2;\n    pool_size = 0;\n    custom_pool = 0;\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (lua_type(L, n) == LUA_TTABLE) {\n\n        /* found the last optional option table */\n\n        lua_getfield(L, n, \"pool_size\");\n\n        if (lua_isnumber(L, -1)) {\n            pool_size = (ngx_int_t) lua_tointeger(L, -1);\n\n            if (pool_size <= 0) {\n                msg = lua_pushfstring(L, \"bad \\\"pool_size\\\" option value: %d\",\n                                      pool_size);\n                return luaL_argerror(L, n, msg);\n            }\n\n        } else if (!lua_isnil(L, -1)) {\n            msg = lua_pushfstring(L, \"bad \\\"pool_size\\\" option type: %s\",\n                                  lua_typename(L, lua_type(L, -1)));\n            return luaL_argerror(L, n, msg);\n        }\n\n        lua_pop(L, 1);\n\n        lua_getfield(L, n, \"backlog\");\n\n        if (lua_isnumber(L, -1)) {\n            backlog = (ngx_int_t) lua_tointeger(L, -1);\n\n            if (backlog < 0) {\n                msg = lua_pushfstring(L, \"bad \\\"backlog\\\" option value: %d\",\n                                      backlog);\n                return luaL_argerror(L, n, msg);\n            }\n\n            /* use default value for pool size if only backlog specified */\n            if (pool_size == 0) {\n                pool_size = llcf->pool_size;\n            }\n        }\n\n        lua_pop(L, 1);\n\n        lua_getfield(L, n, \"pool\");\n\n        switch (lua_type(L, -1)) {\n        case LUA_TNUMBER:\n            lua_tostring(L, -1);\n            /* FALLTHROUGH */\n\n        case LUA_TSTRING:\n            custom_pool = 1;\n\n            lua_pushvalue(L, -1);\n            lua_rawseti(L, 1, SOCKET_KEY_INDEX);\n\n            key_index = n + 1;\n\n            break;\n\n        case LUA_TNIL:\n            lua_pop(L, 2);\n            break;\n\n        default:\n            msg = lua_pushfstring(L, \"bad \\\"pool\\\" option type: %s\",\n                                  luaL_typename(L, -1));\n            luaL_argerror(L, n, msg);\n            break;\n        }\n\n        n--;\n    }\n\n    /* the fourth argument is not a table */\n    if (n == 4) {\n        lua_pop(L, 1);\n        n--;\n    }\n\n    /* most popular suit: host:port */\n    if (n == 3 && lua_isnumber(L, 3)) {\n\n        /* Hit the following parameter combination:\n         * sock:connect(\"127.0.0.1\", port)\n         * sock:connect(\"127.0.0.1\", port, opts)\n         * sock:connect(\"unix:/path\", port)\n         * sock:connect(\"unix:/path\", port, opts) */\n\n        port = (int) lua_tointeger(L, 3);\n\n        if (port < 0 || port > 65535) {\n            lua_pushnil(L);\n            lua_pushfstring(L, \"bad port number: %d\", port);\n            return 2;\n        }\n\n        if (!custom_pool) {\n            lua_pushliteral(L, \":\");\n            lua_insert(L, 3);\n            lua_concat(L, 3);\n        }\n\n        dd(\"socket key: %s\", lua_tostring(L, -1));\n\n    } else if (len >= 5 && ngx_strncasecmp(p, (u_char *) \"unix:\", 5) == 0) {\n\n        /* Hit the following parameter combination:\n         * sock:connect(\"unix:/path\")\n         * sock:connect(\"unix:/path\", nil)\n         * sock:connect(\"unix:/path\", opts)\n         * sock:connect(\"unix:/path\", nil, opts) */\n\n        port = 0;\n\n    } else {\n\n        /* Ban the following parameter combination:\n         * sock:connect(\"127.0.0.1\")\n         * sock:connect(\"127.0.0.1\", nil)\n         * sock:connect(\"127.0.0.1\", opts)\n         * sock:connect(\"127.0.0.1\", nil, opts) */\n\n        lua_pushnil(L);\n        lua_pushfstring(L, \"missing the port number\");\n        return 2;\n    }\n\n    if (!custom_pool) {\n        /* the key's index is 2 */\n\n        lua_pushvalue(L, 2);\n        lua_rawseti(L, 1, SOCKET_KEY_INDEX);\n    }\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    if (u) {\n        if (u->request && u->request != r) {\n            return luaL_error(L, \"bad request\");\n        }\n\n        ngx_http_lua_socket_check_busy_connecting(r, u, L);\n        ngx_http_lua_socket_check_busy_reading(r, u, L);\n        ngx_http_lua_socket_check_busy_writing(r, u, L);\n\n        if (u->body_downstream || u->raw_downstream) {\n            return luaL_error(L, \"attempt to re-connect a request socket\");\n        }\n\n        if (u->peer.connection) {\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"lua tcp socket reconnect without shutting down\");\n\n            ngx_http_lua_socket_tcp_finalize(r, u);\n        }\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua reuse socket upstream ctx\");\n\n    } else {\n        u = lua_newuserdata(L, sizeof(ngx_http_lua_socket_tcp_upstream_t));\n        if (u == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n#if 1\n        lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                              upstream_udata_metatable_key));\n        lua_rawget(L, LUA_REGISTRYINDEX);\n        lua_setmetatable(L, -2);\n#endif\n\n        lua_rawseti(L, 1, SOCKET_CTX_INDEX);\n    }\n\n    ngx_memzero(u, sizeof(ngx_http_lua_socket_tcp_upstream_t));\n\n    u->request = r; /* set the controlling request */\n\n    u->conf = llcf;\n    if (len < sizeof(u->host)) {\n        ngx_memcpy(u->host, p, len);\n        u->host[len] = '\\0';\n\n    } else {\n        ngx_memcpy(u->host, p, sizeof(u->host) - 4);\n        u->host[sizeof(u->host) - 4] = '.';\n        u->host[sizeof(u->host) - 3] = '.';\n        u->host[sizeof(u->host) - 2] = '.';\n        u->host[sizeof(u->host) - 1] = '\\0';\n    }\n\n    u->log = *r->connection->log;\n    pc = &u->peer;\n    pc->sockaddr = (struct sockaddr *) &u->sockaddr;\n    pc->log = &u->log;\n    pc->log_error = NGX_ERROR_ERR;\n\n    dd(\"lua peer connection log: %p\", pc->log);\n\n    lua_rawgeti(L, 1, SOCKET_BIND_INDEX);\n    local = lua_touserdata(L, -1);\n\n    if (local != NULL) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket sock:connect ip: %V\", &local->name);\n    }\n\n    lua_pop(L, 1);\n\n    if (local) {\n        u->peer.local = local;\n    }\n\n    lua_rawgeti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX);\n    lua_rawgeti(L, 1, SOCKET_SEND_TIMEOUT_INDEX);\n    lua_rawgeti(L, 1, SOCKET_READ_TIMEOUT_INDEX);\n\n    read_timeout = (ngx_int_t) lua_tointeger(L, -1);\n    send_timeout = (ngx_int_t) lua_tointeger(L, -2);\n    connect_timeout = (ngx_int_t) lua_tointeger(L, -3);\n\n    lua_pop(L, 3);\n\n    if (connect_timeout > 0) {\n        u->connect_timeout = (ngx_msec_t) connect_timeout;\n\n    } else {\n        u->connect_timeout = u->conf->connect_timeout;\n    }\n\n    if (send_timeout > 0) {\n        u->send_timeout = (ngx_msec_t) send_timeout;\n\n    } else {\n        u->send_timeout = u->conf->send_timeout;\n    }\n\n    if (read_timeout > 0) {\n        u->read_timeout = (ngx_msec_t) read_timeout;\n\n    } else {\n        u->read_timeout = u->conf->read_timeout;\n    }\n\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(socket_pool_key));\n    lua_rawget(L, LUA_REGISTRYINDEX); /* table */\n    lua_pushvalue(L, key_index); /* key */\n\n    lua_rawget(L, -2);\n    spool = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    if (spool != NULL) {\n        u->socket_pool = spool;\n\n    } else if (pool_size > 0) {\n        lua_pushvalue(L, key_index);\n        key.data = (u_char *) lua_tolstring(L, -1, &key.len);\n\n        ngx_http_lua_socket_tcp_create_socket_pool(L, r, key, pool_size,\n                                                   backlog, &spool);\n        u->socket_pool = spool;\n    }\n\n    return ngx_http_lua_socket_tcp_connect_helper(L, u, r, ctx, p,\n                                                  len, port, 0);\n}\n\n\nstatic void\nngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx)\n{\n    ngx_http_request_t                  *r;\n    ngx_connection_t                    *c;\n    ngx_http_upstream_resolved_t        *ur;\n    ngx_http_lua_ctx_t                  *lctx;\n    lua_State                           *L;\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n    u_char                              *p;\n    size_t                               len;\n    socklen_t                            socklen;\n    struct sockaddr                     *sockaddr;\n    ngx_uint_t                           i;\n    unsigned                             waiting;\n\n    u = ctx->data;\n    r = u->request;\n    c = r->connection;\n    ur = u->resolved;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"lua tcp socket resolve handler\");\n\n    lctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (lctx == NULL) {\n        return;\n    }\n\n    lctx->cur_co_ctx = u->write_co_ctx;\n\n    u->write_co_ctx->cleanup = NULL;\n\n    L = lctx->cur_co_ctx->co;\n\n    waiting = u->conn_waiting;\n\n    if (ctx->state) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"lua tcp socket resolver error: %s \"\n                       \"(connect waiting: %d)\",\n                       ngx_resolver_strerror(ctx->state), (int) waiting);\n\n        lua_pushnil(L);\n        lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len);\n        lua_pushfstring(L, \" could not be resolved (%d: %s)\",\n                        (int) ctx->state,\n                        ngx_resolver_strerror(ctx->state));\n        lua_concat(L, 2);\n\n        u->write_prepare_retvals =\n                                ngx_http_lua_socket_conn_error_retval_handler;\n        ngx_http_lua_socket_handle_conn_error(r, u,\n                                              NGX_HTTP_LUA_SOCKET_FT_RESOLVER);\n\n        if (waiting) {\n            ngx_http_run_posted_requests(c);\n        }\n\n        return;\n    }\n\n    ur->naddrs = ctx->naddrs;\n    ur->addrs = ctx->addrs;\n\n#if (NGX_DEBUG)\n    {\n        u_char      text[NGX_SOCKADDR_STRLEN];\n        ngx_str_t   addr;\n        ngx_uint_t  i;\n\n        addr.data = text;\n\n        for (i = 0; i < ctx->naddrs; i++) {\n            addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr,\n                                     ur->addrs[i].socklen, text,\n                                     NGX_SOCKADDR_STRLEN, 0);\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"name was resolved to %V\", &addr);\n        }\n    }\n#endif\n\n    ngx_http_lua_assert(ur->naddrs > 0);\n\n    if (ur->naddrs == 1) {\n        i = 0;\n\n    } else {\n        i = ngx_random() % ur->naddrs;\n    }\n\n    dd(\"selected addr index: %d\", (int) i);\n\n    socklen = ur->addrs[i].socklen;\n\n    sockaddr = (struct sockaddr *) &u->sockaddr;\n\n    ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen);\n\n    switch (sockaddr->sa_family) {\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port);\n        break;\n#endif\n    default: /* AF_INET */\n        ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port);\n    }\n\n    p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN);\n    if (p == NULL) {\n        goto nomem;\n    }\n\n    len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);\n    ur->sockaddr = sockaddr;\n    ur->socklen = socklen;\n\n    ur->host.data = p;\n    ur->host.len = len;\n    ur->naddrs = 1;\n\n    ngx_resolve_name_done(ctx);\n    ur->ctx = NULL;\n\n    u->conn_waiting = 0;\n    u->write_co_ctx = NULL;\n\n    if (waiting) {\n        lctx->resume_handler = ngx_http_lua_socket_tcp_conn_resume;\n        r->write_event_handler(r);\n        ngx_http_run_posted_requests(c);\n\n    } else {\n        (void) ngx_http_lua_socket_resolve_retval_handler(r, u, L);\n    }\n\n    return;\n\nnomem:\n\n    if (ur->ctx) {\n        ngx_resolve_name_done(ctx);\n        ur->ctx = NULL;\n    }\n\n    u->write_prepare_retvals = ngx_http_lua_socket_conn_error_retval_handler;\n    ngx_http_lua_socket_handle_conn_error(r, u,\n                                          NGX_HTTP_LUA_SOCKET_FT_NOMEM);\n\n    if (waiting) {\n        dd(\"run posted requests\");\n        ngx_http_run_posted_requests(c);\n\n    } else {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"no memory\");\n    }\n}\n\n\nstatic void\nngx_http_lua_socket_init_peer_connection_addr_text(ngx_peer_connection_t *pc)\n{\n    ngx_connection_t            *c;\n    size_t                       addr_text_max_len;\n\n    c = pc->connection;\n\n    switch (pc->sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        addr_text_max_len = NGX_INET6_ADDRSTRLEN;\n        break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    case AF_UNIX:\n        addr_text_max_len = NGX_UNIX_ADDRSTRLEN;\n        break;\n#endif\n\n    case AF_INET:\n        addr_text_max_len = NGX_INET_ADDRSTRLEN;\n        break;\n\n    default:\n        addr_text_max_len = NGX_SOCKADDR_STRLEN;\n        break;\n    }\n\n    c->addr_text.data = ngx_pnalloc(c->pool, addr_text_max_len);\n    if (c->addr_text.data == NULL) {\n        ngx_log_error(NGX_LOG_ERR, pc->log, 0,\n                      \"init peer connection addr_text failed: no memory\");\n        return;\n    }\n\n    c->addr_text.len = ngx_sock_ntop(pc->sockaddr, pc->socklen,\n                                     c->addr_text.data,\n                                     addr_text_max_len, 0);\n}\n\n\nstatic int\nngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L)\n{\n    ngx_http_lua_ctx_t              *ctx;\n    ngx_peer_connection_t           *pc;\n    ngx_connection_t                *c;\n    ngx_http_cleanup_t              *cln;\n    ngx_http_upstream_resolved_t    *ur;\n    ngx_int_t                        rc;\n    ngx_http_lua_co_ctx_t           *coctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket resolve retval handler\");\n\n    if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_RESOLVER) {\n        return 2;\n    }\n\n    pc = &u->peer;\n\n    ur = u->resolved;\n\n    if (ur->sockaddr) {\n        pc->sockaddr = ur->sockaddr;\n        pc->socklen = ur->socklen;\n        pc->name = &ur->host;\n        ngx_memcpy(&u->sockaddr, ur->sockaddr, ur->socklen);\n\n    } else {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"resolver not working\");\n        return 2;\n    }\n\n    pc->get = ngx_http_lua_socket_tcp_get_peer;\n\n    rc = ngx_event_connect_peer(pc);\n\n    if (rc == NGX_ERROR) {\n        u->socket_errno = ngx_socket_errno;\n    }\n\n    if (u->cleanup == NULL) {\n        cln = ngx_http_lua_cleanup_add(r, 0);\n        if (cln == NULL) {\n            u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR;\n            lua_pushnil(L);\n            lua_pushliteral(L, \"no memory\");\n            return 2;\n        }\n\n        cln->handler = ngx_http_lua_socket_tcp_cleanup;\n        cln->data = u;\n        u->cleanup = &cln->handler;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket connect: %i\", rc);\n\n    if (rc == NGX_ERROR) {\n        return ngx_http_lua_socket_conn_error_retval_handler(r, u, L);\n    }\n\n    if (rc == NGX_BUSY) {\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR;\n        lua_pushnil(L);\n        lua_pushliteral(L, \"no live connection\");\n        return 2;\n    }\n\n    if (rc == NGX_DECLINED) {\n        dd(\"socket errno: %d\", (int) ngx_socket_errno);\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR;\n        u->socket_errno = ngx_socket_errno;\n        return ngx_http_lua_socket_conn_error_retval_handler(r, u, L);\n    }\n\n    /* rc == NGX_OK || rc == NGX_AGAIN */\n\n    c = pc->connection;\n\n    c->data = u;\n\n    c->write->handler = ngx_http_lua_socket_tcp_handler;\n    c->read->handler = ngx_http_lua_socket_tcp_handler;\n\n    u->write_event_handler = ngx_http_lua_socket_connected_handler;\n    u->read_event_handler = ngx_http_lua_socket_connected_handler;\n\n    c->sendfile &= r->connection->sendfile;\n\n    if (c->pool == NULL) {\n\n        /* we need separate pool here to be able to cache SSL connections */\n\n        c->pool = ngx_create_pool(128, r->connection->log);\n        if (c->pool == NULL) {\n            return ngx_http_lua_socket_prepare_error_retvals(r, u, L,\n                                                NGX_HTTP_LUA_SOCKET_FT_NOMEM);\n        }\n    }\n\n    c->log = r->connection->log;\n    c->pool->log = c->log;\n    c->read->log = c->log;\n    c->write->log = c->log;\n\n    /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */\n\n#if 0\n    u->writer.out = NULL;\n    u->writer.last = &u->writer.out;\n#endif\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    coctx = ctx->cur_co_ctx;\n\n    dd(\"setting data to %p\", u);\n\n    if (rc == NGX_OK) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket connected: fd:%d\", (int) c->fd);\n\n        /* We should delete the current write/read event\n         * here because the socket object may not be used immediately\n         * on the Lua land, thus causing hot spin around level triggered\n         * event poll and wasting CPU cycles. */\n\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            ngx_http_lua_socket_handle_conn_error(r, u,\n                                                  NGX_HTTP_LUA_SOCKET_FT_ERROR);\n            lua_pushnil(L);\n            lua_pushliteral(L, \"failed to handle write event\");\n            return 2;\n        }\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_http_lua_socket_handle_conn_error(r, u,\n                                                  NGX_HTTP_LUA_SOCKET_FT_ERROR);\n            lua_pushnil(L);\n            lua_pushliteral(L, \"failed to handle read event\");\n            return 2;\n        }\n\n        u->read_event_handler = ngx_http_lua_socket_dummy_handler;\n        u->write_event_handler = ngx_http_lua_socket_dummy_handler;\n\n        lua_pushinteger(L, 1);\n        return 1;\n    }\n\n    /* rc == NGX_AGAIN */\n\n    ngx_http_lua_cleanup_pending_operation(coctx);\n    coctx->cleanup = ngx_http_lua_coctx_cleanup;\n    coctx->data = u;\n\n    ngx_add_timer(c->write, u->connect_timeout);\n\n    u->write_co_ctx = ctx->cur_co_ctx;\n    u->conn_waiting = 1;\n    u->write_prepare_retvals = ngx_http_lua_socket_tcp_conn_retval_handler;\n\n    dd(\"setting data to %p\", u);\n\n    if (ctx->entered_content_phase) {\n        r->write_event_handler = ngx_http_lua_content_wev_handler;\n\n    } else {\n        r->write_event_handler = ngx_http_core_run_phases;\n    }\n\n    return NGX_AGAIN;\n}\n\n\nstatic int\nngx_http_lua_socket_conn_error_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L)\n{\n    ngx_uint_t      ft_type;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket error retval handler\");\n\n    if (u->write_co_ctx) {\n        u->write_co_ctx->cleanup = NULL;\n    }\n\n    ngx_http_lua_socket_tcp_finalize(r, u);\n\n    ft_type = u->ft_type;\n    u->ft_type = 0;\n    return ngx_http_lua_socket_prepare_error_retvals(r, u, L, ft_type);\n}\n\n\n#if (NGX_HTTP_SSL)\n\nstatic const char *\nngx_http_lua_socket_tcp_check_busy(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, unsigned int ops)\n{\n    if ((ops & SOCKET_OP_CONNECT) && u->conn_waiting) {\n        return \"socket busy connecting\";\n    }\n\n    if ((ops & SOCKET_OP_READ) && u->read_waiting) {\n        return \"socket busy reading\";\n    }\n\n    if ((ops & SOCKET_OP_WRITE)\n        && (u->write_waiting\n            || (u->raw_downstream\n                && (r->connection->buffered & NGX_HTTP_LOWLEVEL_BUFFERED))))\n    {\n        return \"socket busy writing\";\n    }\n\n    return NULL;\n}\n\n\nint\nngx_http_lua_socket_tcp_get_ssl_session(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, ngx_ssl_session_t **sess,\n    const char **errmsg)\n{\n    ngx_connection_t                      *c;\n    ngx_ssl_session_t                     *ssl_session;\n\n    *sess = NULL;\n    if (u == NULL\n        || u->peer.connection == NULL\n        || (u->read_closed && u->write_closed))\n    {\n        *errmsg = \"closed\";\n        return NGX_ERROR;\n    }\n\n    c = u->peer.connection;\n    ssl_session = ngx_ssl_get_session(c);\n    if (ssl_session == NULL) {\n        *errmsg = \"no session\";\n        return NGX_ERROR;\n    }\n\n    if (!SSL_SESSION_is_resumable(ssl_session)) {\n        ngx_ssl_free_session(ssl_session);\n        *errmsg = \"not resumable\";\n        return NGX_ERROR;\n    }\n\n    *sess = ssl_session;\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_socket_tcp_sslhandshake(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, ngx_ssl_session_t *sess,\n    int enable_session_reuse, ngx_str_t *server_name, int verify,\n    int ocsp_status_req, STACK_OF(X509) *chain, EVP_PKEY *pkey,\n    const char **errmsg)\n{\n    ngx_int_t                rc, i;\n    ngx_connection_t        *c;\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_http_lua_co_ctx_t   *coctx;\n    const char              *busy_msg;\n    ngx_ssl_conn_t          *ssl_conn;\n    X509                    *x509;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket ssl handshake\");\n\n    if (u == NULL\n        || u->peer.connection == NULL\n        || u->read_closed\n        || u->write_closed)\n    {\n        *errmsg = \"closed\";\n        return NGX_ERROR;\n    }\n\n    if (u->request != r) {\n        *errmsg = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    busy_msg = ngx_http_lua_socket_tcp_check_busy(r, u, SOCKET_OP_CONNECT\n                                                  | SOCKET_OP_READ\n                                                  | SOCKET_OP_WRITE);\n    if (busy_msg != NULL) {\n        *errmsg = busy_msg;\n        return NGX_ERROR;\n    }\n\n    if (u->raw_downstream || u->body_downstream) {\n        *errmsg = \"not supported for downstream sockets\";\n        return NGX_ERROR;\n    }\n\n    c = u->peer.connection;\n\n    u->ssl_session_reuse = 1;\n\n    if (c->ssl && c->ssl->handshaked) {\n        if (sess != NULL) {\n            return NGX_DONE;\n        }\n\n        u->ssl_session_reuse = enable_session_reuse;\n\n        (void) ngx_http_lua_ssl_handshake_retval_handler(r, u, NULL);\n\n        return NGX_OK;\n    }\n\n    if (ngx_ssl_create_connection(u->conf->ssl, c,\n                                  NGX_SSL_BUFFER|NGX_SSL_CLIENT)\n        != NGX_OK)\n    {\n        *errmsg = \"failed to create ssl connection\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_HTTP_LUA_FFI_NO_REQ_CTX;\n    }\n\n    coctx = ctx->cur_co_ctx;\n\n    c->sendfile = 0;\n\n    if (sess != NULL) {\n        if (ngx_ssl_set_session(c, sess) != NGX_OK) {\n            *errmsg = \"ssl set session failed\";\n            return NGX_ERROR;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"lua ssl set session: %p\", sess);\n\n    } else {\n        u->ssl_session_reuse = enable_session_reuse;\n    }\n\n    if (chain != NULL) {\n        ngx_http_lua_assert(pkey != NULL); /* ensured by resty.core */\n\n        if (sk_X509_num(chain) < 1) {\n            ERR_clear_error();\n            *errmsg = \"invalid client certificate chain\";\n            return NGX_ERROR;\n        }\n\n        x509 = sk_X509_value(chain, 0);\n        if (x509 == NULL) {\n            ERR_clear_error();\n            *errmsg = \"ssl fetch client certificate from chain failed\";\n            return NGX_ERROR;\n        }\n\n        if (SSL_use_certificate(ssl_conn, x509) == 0) {\n            ERR_clear_error();\n            *errmsg = \"ssl set client certificate failed\";\n            return NGX_ERROR;\n        }\n\n        /* read rest of the chain */\n\n        for (i = 1; i < (ngx_int_t) sk_X509_num(chain); i++) {\n            x509 = sk_X509_value(chain, i);\n            if (x509 == NULL) {\n                ERR_clear_error();\n                *errmsg = \"ssl fetch client intermediate certificate from \"\n                          \"chain failed\";\n                return NGX_ERROR;\n            }\n\n            if (SSL_add1_chain_cert(ssl_conn, x509) == 0) {\n                ERR_clear_error();\n                *errmsg = \"ssl set client intermediate certificate failed\";\n                return NGX_ERROR;\n            }\n        }\n\n        if (SSL_use_PrivateKey(ssl_conn, pkey) == 0) {\n            ERR_clear_error();\n            *errmsg = \"ssl set client private key failed\";\n            return NGX_ERROR;\n        }\n    }\n\n    if (server_name != NULL && server_name->data != NULL) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua ssl server name: \\\"%V\\\"\", server_name);\n\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n        if (SSL_set_tlsext_host_name(c->ssl->connection,\n                                     (char *) server_name->data)\n            == 0)\n        {\n            *errmsg = \"SSL_set_tlsext_host_name failed\";\n            return NGX_ERROR;\n        }\n\n#else\n        *errmsg = \"no TLS extension support\";\n        return NGX_ERROR;\n#endif\n    }\n\n    u->ssl_verify = verify;\n\n    if (ocsp_status_req) {\n#ifdef NGX_HTTP_LUA_USE_OCSP\n        SSL_set_tlsext_status_type(c->ssl->connection,\n                                   TLSEXT_STATUSTYPE_ocsp);\n\n#else\n        *errmsg = \"no OCSP support\";\n        return NGX_ERROR;\n#endif\n    }\n\n    if (server_name == NULL || server_name->len == 0) {\n        u->ssl_name.len = 0;\n\n    } else {\n        if (u->ssl_name.data) {\n            /* buffer already allocated */\n\n            if (u->ssl_name.len >= server_name->len) {\n                /* reuse it */\n                ngx_memcpy(u->ssl_name.data, server_name->data,\n                           server_name->len);\n                u->ssl_name.len = server_name->len;\n\n            } else {\n                ngx_free(u->ssl_name.data);\n                goto new_ssl_name;\n            }\n\n        } else {\n\nnew_ssl_name:\n\n            u->ssl_name.data = ngx_alloc(server_name->len, ngx_cycle->log);\n            if (u->ssl_name.data == NULL) {\n                u->ssl_name.len = 0;\n                *errmsg = \"no memory\";\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(u->ssl_name.data, server_name->data, server_name->len);\n            u->ssl_name.len = server_name->len;\n        }\n    }\n\n    u->write_co_ctx = coctx;\n\n#if 0\n#ifdef NGX_HTTP_LUA_USE_OCSP\n    SSL_set_tlsext_status_type(c->ssl->connection, TLSEXT_STATUSTYPE_ocsp);\n#endif\n#endif\n\n    rc = ngx_ssl_handshake(c);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"ngx_ssl_handshake returned: %d\", rc);\n\n    if (rc == NGX_AGAIN) {\n        if (c->write->timer_set) {\n            ngx_del_timer(c->write);\n        }\n\n        ngx_add_timer(c->read, u->connect_timeout);\n\n        u->conn_waiting = 1;\n        u->write_prepare_retvals = ngx_http_lua_ssl_handshake_retval_handler;\n\n        ngx_http_lua_cleanup_pending_operation(coctx);\n        coctx->cleanup = ngx_http_lua_coctx_cleanup;\n        coctx->data = u;\n\n        c->ssl->handler = ngx_http_lua_ssl_handshake_handler;\n\n        if (ctx->entered_content_phase) {\n            r->write_event_handler = ngx_http_lua_content_wev_handler;\n\n        } else {\n            r->write_event_handler = ngx_http_core_run_phases;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    ngx_http_lua_ssl_handshake_handler(c);\n\n    if (rc == NGX_ERROR) {\n        *errmsg = u->error_ret;\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_lua_ssl_handshake_handler(ngx_connection_t *c)\n{\n    int                          waiting;\n    ngx_int_t                    rc;\n    ngx_connection_t            *dc;  /* downstream connection */\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n\n    u = c->data;\n    r = u->request;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return;\n    }\n\n    c->write->handler = ngx_http_lua_socket_tcp_handler;\n    c->read->handler = ngx_http_lua_socket_tcp_handler;\n\n    waiting = u->conn_waiting;\n\n    dc = r->connection;\n\n    if (c->read->timedout) {\n        u->error_ret = \"timeout\";\n        goto failed;\n    }\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    if (c->ssl->handshaked) {\n        if (u->ssl_verify) {\n            rc = SSL_get_verify_result(c->ssl->connection);\n\n            if (rc != X509_V_OK) {\n                u->error_ret = X509_verify_cert_error_string(rc);\n                u->openssl_error_code_ret = rc;\n\n                llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n                if (llcf->log_socket_errors) {\n                    ngx_log_error(NGX_LOG_ERR, u->peer.log, 0, \"lua ssl \"\n                                  \"certificate verify error: (%d: %s)\",\n                                  rc, u->error_ret);\n                }\n\n                goto failed;\n            }\n\n#if (nginx_version >= 1007000)\n\n            if (u->ssl_name.len\n                && ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK)\n            {\n                u->error_ret = \"certificate host mismatch\";\n\n                llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n                if (llcf->log_socket_errors) {\n                    ngx_log_error(NGX_LOG_ERR, u->peer.log, 0, \"lua ssl \"\n                                  \"certificate does not match host \\\"%V\\\"\",\n                                  &u->ssl_name);\n                }\n\n                goto failed;\n            }\n\n#endif\n        }\n\n        if (waiting) {\n            ngx_http_lua_socket_handle_conn_success(r, u);\n\n        } else {\n            (void) ngx_http_lua_ssl_handshake_retval_handler(r, u, NULL);\n        }\n\n        if (waiting) {\n            ngx_http_run_posted_requests(dc);\n        }\n\n        return;\n    }\n\n    u->error_ret = \"handshake failed\";\n\nfailed:\n\n    if (waiting) {\n        u->write_prepare_retvals =\n            ngx_http_lua_socket_conn_error_retval_handler;\n        ngx_http_lua_socket_handle_conn_error(r, u, NGX_HTTP_LUA_SOCKET_FT_SSL);\n        ngx_http_run_posted_requests(dc);\n\n    } else {\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_SSL;\n\n        (void) ngx_http_lua_socket_conn_error_retval_handler(r, u, NULL);\n    }\n}\n\n\nint\nngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, ngx_ssl_session_t **sess,\n    const char **errmsg, int *openssl_error_code)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua cosocket get SSL handshake result for upstream: %p\", u);\n\n    if (u->error_ret != NULL) {\n        *errmsg = u->error_ret;\n        *openssl_error_code = u->openssl_error_code_ret;\n\n        return NGX_ERROR;\n    }\n\n    *sess = u->ssl_session_ret;\n\n    return NGX_OK;\n}\n\n\nstatic int\nngx_http_lua_ssl_handshake_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L)\n{\n    ngx_connection_t            *c;\n    ngx_ssl_session_t           *ssl_session;\n\n    if (!u->ssl_session_reuse) {\n        return 0;\n    }\n\n    c = u->peer.connection;\n\n    ssl_session = ngx_ssl_get_session(c);\n    if (ssl_session == NULL) {\n        u->ssl_session_ret = NULL;\n\n    } else {\n        u->ssl_session_ret = ssl_session;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"lua ssl save session: %p\", ssl_session);\n    }\n\n    return 0;\n}\n\n\nvoid\nngx_http_lua_ffi_ssl_free_session(ngx_ssl_session_t *sess)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua ssl free session: %p\", sess);\n\n    ngx_ssl_free_session(sess);\n}\n\n\nint\nngx_http_lua_ffi_socket_tcp_get_ssl_pointer(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, ngx_ssl_conn_t **pssl,\n    const char **errmsg)\n{\n    ngx_connection_t                      *c;\n\n    *pssl = NULL;\n    if (u == NULL\n        || u->peer.connection == NULL\n        || (u->read_closed && u->write_closed))\n    {\n        *errmsg = \"closed\";\n        return NGX_ERROR;\n    }\n\n    c = u->peer.connection;\n    if (c == NULL || c->ssl == NULL || c->ssl->connection == NULL) {\n        *errmsg = \"no ssl connection\";\n        return NGX_ERROR;\n    }\n\n    *pssl = c->ssl->connection;\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_socket_tcp_get_ssl_ctx(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, SSL_CTX **pctx,\n    const char **errmsg)\n{\n    ngx_connection_t                      *c;\n\n    *pctx = NULL;\n    if (u == NULL\n        || u->peer.connection == NULL\n        || (u->read_closed && u->write_closed))\n    {\n        *errmsg = \"closed\";\n        return NGX_ERROR;\n    }\n\n    c = u->peer.connection;\n    if (c == NULL || c->ssl == NULL || c->ssl->session_ctx == NULL) {\n        *errmsg = \"no ssl context\";\n        return NGX_ERROR;\n    }\n\n    *pctx = c->ssl->session_ctx;\n\n    return NGX_OK;\n}\n\n\n#endif  /* NGX_HTTP_SSL */\n\n\nstatic int\nngx_http_lua_socket_read_error_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L)\n{\n    ngx_uint_t          ft_type;\n\n    if (u->read_co_ctx) {\n        u->read_co_ctx->cleanup = NULL;\n    }\n\n    ft_type = u->ft_type;\n    u->ft_type = 0;\n\n    if (u->no_close) {\n        u->no_close = 0;\n\n    } else {\n        ngx_http_lua_socket_tcp_finalize_read_part(r, u);\n    }\n\n    return ngx_http_lua_socket_prepare_error_retvals(r, u, L, ft_type);\n}\n\n\nstatic int\nngx_http_lua_socket_write_error_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L)\n{\n    ngx_uint_t          ft_type;\n\n    if (u->write_co_ctx) {\n        u->write_co_ctx->cleanup = NULL;\n    }\n\n    ngx_http_lua_socket_tcp_finalize_write_part(r, u);\n\n    ft_type = u->ft_type;\n    u->ft_type = 0;\n    return ngx_http_lua_socket_prepare_error_retvals(r, u, L, ft_type);\n}\n\n\nstatic int\nngx_http_lua_socket_prepare_error_retvals(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L, ngx_uint_t ft_type)\n{\n    u_char           errstr[NGX_MAX_ERROR_STR];\n    u_char          *p;\n\n    if (ft_type & NGX_HTTP_LUA_SOCKET_FT_RESOLVER) {\n        return 2;\n    }\n\n    if (ft_type & NGX_HTTP_LUA_SOCKET_FT_SSL) {\n        return 0;\n    }\n\n    lua_pushnil(L);\n\n    if (ft_type & NGX_HTTP_LUA_SOCKET_FT_TIMEOUT) {\n        lua_pushliteral(L, \"timeout\");\n\n    } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_CLOSED) {\n        lua_pushliteral(L, \"closed\");\n\n    } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_BUFTOOSMALL) {\n        lua_pushliteral(L, \"buffer too small\");\n\n    } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_NOMEM) {\n        lua_pushliteral(L, \"no memory\");\n\n    } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT) {\n        lua_pushliteral(L, \"client aborted\");\n\n    } else {\n\n        if (u->socket_errno) {\n            p = ngx_strerror(u->socket_errno, errstr, sizeof(errstr));\n            /* for compatibility with LuaSocket */\n            ngx_strlow(errstr, errstr, p - errstr);\n            lua_pushlstring(L, (char *) errstr, p - errstr);\n\n        } else {\n            lua_pushliteral(L, \"error\");\n        }\n    }\n\n    return 2;\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_conn_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L)\n{\n    if (u->ft_type) {\n        return ngx_http_lua_socket_conn_error_retval_handler(r, u, L);\n    }\n\n    lua_pushinteger(L, 1);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_receive_helper(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L)\n{\n    ngx_int_t                            rc;\n    ngx_http_lua_ctx_t                  *ctx;\n    ngx_http_lua_co_ctx_t               *coctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (u->bufs_in == NULL) {\n        u->bufs_in =\n            ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,\n                                            &ctx->free_recv_bufs,\n                                            u->conf->buffer_size);\n\n        if (u->bufs_in == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        u->buf_in = u->bufs_in;\n        u->buffer = *u->buf_in->buf;\n    }\n\n    dd(\"tcp receive: buf_in: %p, bufs_in: %p\", u->buf_in, u->bufs_in);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket read timeout: %M\", u->read_timeout);\n\n    if (u->raw_downstream || u->body_downstream) {\n        r->read_event_handler = ngx_http_lua_req_socket_rev_handler;\n    }\n\n    u->read_waiting = 0;\n    u->read_co_ctx = NULL;\n\n    ngx_http_lua_socket_tcp_read_prepare(r, u, u, L);\n\n    rc = ngx_http_lua_socket_tcp_read(r, u);\n\n    if (rc == NGX_ERROR) {\n        dd(\"read failed: %d\", (int) u->ft_type);\n        rc = ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L);\n        dd(\"tcp receive retval returned: %d\", (int) rc);\n        return rc;\n    }\n\n    if (rc == NGX_OK) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket receive done in a single run\");\n\n        return ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L);\n    }\n\n    /* rc == NGX_AGAIN */\n\n    u->read_event_handler = ngx_http_lua_socket_read_handler;\n\n    coctx = ctx->cur_co_ctx;\n\n    ngx_http_lua_cleanup_pending_operation(coctx);\n    coctx->cleanup = ngx_http_lua_coctx_cleanup;\n    coctx->data = u;\n\n    if (ctx->entered_content_phase) {\n        r->write_event_handler = ngx_http_lua_content_wev_handler;\n\n    } else {\n        r->write_event_handler = ngx_http_core_run_phases;\n    }\n\n    u->read_co_ctx = coctx;\n    u->read_waiting = 1;\n    u->read_prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler;\n\n    dd(\"setting data to %p, coctx:%p\", u, coctx);\n\n    if (u->raw_downstream || u->body_downstream) {\n        ctx->downstream = u;\n    }\n\n    return lua_yield(L, 0);\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_receiveany(lua_State *L)\n{\n    int                                  n;\n    lua_Integer                          bytes;\n    ngx_http_request_t                  *r;\n    ngx_http_lua_loc_conf_t             *llcf;\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n\n    n = lua_gettop(L);\n    if (n != 2) {\n        return luaL_error(L, \"expecting 2 arguments \"\n                          \"(including the object), but got %d\", n);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n\n    if (u == NULL || u->peer.connection == NULL || u->read_closed) {\n\n        llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n        if (llcf->log_socket_errors) {\n            ngx_log_t  *log;\n\n            log = r->connection->log;\n            if (u != NULL && u->peer.connection != NULL) {\n                log = u->peer.log;\n            }\n\n            ngx_log_error(NGX_LOG_ERR, log, 0,\n                          \"attempt to receive data on a closed socket: u:%p, \"\n                          \"c:%p, ft:%d eof:%d\",\n                          u, u ? u->peer.connection : NULL,\n                          u ? (int) u->ft_type : 0, u ? (int) u->eof : 0);\n        }\n\n        lua_pushnil(L);\n        lua_pushliteral(L, \"closed\");\n        return 2;\n    }\n\n    if (u->request != r) {\n        return luaL_error(L, \"bad request\");\n    }\n\n    ngx_http_lua_socket_check_busy_connecting(r, u, L);\n    ngx_http_lua_socket_check_busy_reading(r, u, L);\n\n    if (!lua_isnumber(L, 2)) {\n        return luaL_argerror(L, 2, \"bad max argument\");\n    }\n\n    bytes = lua_tointeger(L, 2);\n    if (bytes <= 0) {\n        return luaL_argerror(L, 2, \"bad max argument\");\n    }\n\n    u->input_filter = ngx_http_lua_socket_read_any;\n    u->rest = (size_t) bytes;\n    u->length = u->rest;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket calling receiveany() method to read at \"\n                   \"most %uz bytes\", u->rest);\n\n    return ngx_http_lua_socket_tcp_receive_helper(r, u, L);\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_receive(lua_State *L)\n{\n    ngx_http_request_t                  *r;\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n    int                                  n;\n    ngx_str_t                            pat;\n    lua_Integer                          bytes;\n    char                                *p;\n    int                                  typ;\n    ngx_http_lua_loc_conf_t             *llcf;\n\n    n = lua_gettop(L);\n    if (n != 1 && n != 2) {\n        return luaL_error(L, \"expecting 1 or 2 arguments \"\n                          \"(including the object), but got %d\", n);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket calling receive() method\");\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n\n    if (u == NULL || u->peer.connection == NULL || u->read_closed) {\n\n        llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n        if (llcf->log_socket_errors) {\n            ngx_log_t  *log;\n\n            log = r->connection->log;\n            if (u != NULL && u->peer.connection != NULL) {\n                log = u->peer.log;\n            }\n\n            ngx_log_error(NGX_LOG_ERR, log, 0,\n                          \"attempt to receive data on a closed socket: u:%p, \"\n                          \"c:%p, ft:%d eof:%d\",\n                          u, u ? u->peer.connection : NULL,\n                          u ? (int) u->ft_type : 0, u ? (int) u->eof : 0);\n        }\n\n        lua_pushnil(L);\n        lua_pushliteral(L, \"closed\");\n        return 2;\n    }\n\n    if (u->request != r) {\n        return luaL_error(L, \"bad request\");\n    }\n\n    ngx_http_lua_socket_check_busy_connecting(r, u, L);\n    ngx_http_lua_socket_check_busy_reading(r, u, L);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket read timeout: %M\", u->read_timeout);\n\n    if (n > 1) {\n        if (lua_isnumber(L, 2)) {\n            typ = LUA_TNUMBER;\n\n        } else {\n            typ = lua_type(L, 2);\n        }\n\n        switch (typ) {\n        case LUA_TSTRING:\n            pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len);\n            if (pat.len != 2 || pat.data[0] != '*') {\n                p = (char *) lua_pushfstring(L, \"bad pattern argument: %s\",\n                                             (char *) pat.data);\n\n                return luaL_argerror(L, 2, p);\n            }\n\n            switch (pat.data[1]) {\n            case 'l':\n                u->input_filter = ngx_http_lua_socket_read_line;\n                break;\n\n            case 'a':\n                u->input_filter = ngx_http_lua_socket_read_all;\n                break;\n\n            default:\n                return luaL_argerror(L, 2, \"bad pattern argument\");\n                break;\n            }\n\n            u->length = 0;\n            u->rest = 0;\n\n            break;\n\n        case LUA_TNUMBER:\n            bytes = lua_tointeger(L, 2);\n            if (bytes < 0) {\n                return luaL_argerror(L, 2, \"bad number argument\");\n            }\n\n#if 1\n            if (bytes == 0) {\n                lua_pushliteral(L, \"\");\n                return 1;\n            }\n#endif\n\n            u->input_filter = ngx_http_lua_socket_read_chunk;\n            u->length = (size_t) bytes;\n            u->rest = u->length;\n\n            break;\n\n        default:\n            return luaL_argerror(L, 2, \"bad argument\");\n            break;\n        }\n\n    } else {\n        u->input_filter = ngx_http_lua_socket_read_line;\n        u->length = 0;\n        u->rest = 0;\n    }\n\n    return ngx_http_lua_socket_tcp_receive_helper(r, u, L);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_read_chunk(void *data, ssize_t bytes)\n{\n    ngx_int_t                                rc;\n    ngx_http_lua_socket_tcp_upstream_t      *u = data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0,\n                   \"lua tcp socket read chunk %z\", bytes);\n\n    rc = ngx_http_lua_read_bytes(&u->buffer, u->buf_in, &u->rest,\n                                 bytes, u->request->connection->log);\n    if (rc == NGX_ERROR) {\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED;\n        return NGX_ERROR;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_read_all(void *data, ssize_t bytes)\n{\n    ngx_http_lua_socket_tcp_upstream_t      *u = data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0,\n                   \"lua tcp socket read all\");\n    return ngx_http_lua_read_all(&u->buffer, u->buf_in, bytes,\n                                 u->request->connection->log);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_read_line(void *data, ssize_t bytes)\n{\n    ngx_http_lua_socket_tcp_upstream_t      *u = data;\n\n    ngx_int_t                    rc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0,\n                   \"lua tcp socket read line\");\n\n    rc = ngx_http_lua_read_line(&u->buffer, u->buf_in, bytes,\n                                u->request->connection->log);\n    if (rc == NGX_ERROR) {\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED;\n        return NGX_ERROR;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_read_any(void *data, ssize_t bytes)\n{\n    ngx_http_lua_socket_tcp_upstream_t      *u = data;\n\n    ngx_int_t                    rc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0,\n                   \"lua tcp socket read any\");\n\n    rc = ngx_http_lua_read_any(&u->buffer, u->buf_in, &u->rest, bytes,\n                               u->request->connection->log);\n    if (rc == NGX_ERROR) {\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED;\n        return NGX_ERROR;\n    }\n\n    return rc;\n}\n\n\nstatic void\nngx_http_lua_socket_tcp_read_prepare(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, void *data, lua_State *L)\n{\n    ngx_http_lua_ctx_t                  *ctx;\n    ngx_chain_t                         *new_cl;\n    ngx_buf_t                           *b;\n    off_t                                size;\n\n    ngx_http_lua_socket_compiled_pattern_t     *cp;\n\n    /* input_filter_ctx doesn't change, no need recovering */\n    if (u->input_filter_ctx == data) {\n        return;\n    }\n\n    /* last input_filter_ctx is null or upstream, no data pending */\n    if (u->input_filter_ctx == NULL || u->input_filter_ctx == u) {\n        u->input_filter_ctx = data;\n        return;\n    }\n\n    /* compiled pattern may be with data pending */\n\n    cp = u->input_filter_ctx;\n    u->input_filter_ctx = data;\n\n    cp->upstream = NULL;\n\n    /* no data pending */\n    if (cp->state <= 0) {\n        return;\n    }\n\n    b = &u->buffer;\n\n    if (b->pos - b->start >= cp->state) {\n        dd(\"pending data in one buffer\");\n\n        b->pos -= cp->state;\n\n        u->buf_in->buf->pos = b->pos;\n        u->buf_in->buf->last = b->pos;\n\n        /* reset dfa state for future matching */\n        cp->state = 0;\n        return;\n    }\n\n    dd(\"pending data in multiple buffers\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    size = ngx_buf_size(b);\n\n    new_cl =\n        ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,\n                                        &ctx->free_recv_bufs,\n                                        cp->state + size);\n\n    if (new_cl == NULL) {\n        luaL_error(L, \"no memory\");\n        return;\n    }\n\n    ngx_memcpy(b, new_cl->buf, sizeof(ngx_buf_t));\n\n    b->last = ngx_copy(b->last, cp->pattern.data, cp->state);\n    b->last = ngx_copy(b->last, u->buf_in->buf->pos, size);\n\n    u->buf_in->next = ctx->free_recv_bufs;\n    ctx->free_recv_bufs = u->buf_in;\n\n    u->bufs_in = new_cl;\n    u->buf_in = new_cl;\n\n    /* reset dfa state for future matching */\n    cp->state = 0;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_tcp_read(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_int_t                    rc;\n    ngx_connection_t            *c;\n    ngx_buf_t                   *b;\n    ngx_event_t                 *rev;\n    off_t                        size;\n    ssize_t                      n;\n    unsigned                     read;\n    off_t                        preread = 0;\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    c = u->peer.connection;\n    rev = c->read;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"lua tcp socket read data: wait:%d\",\n                   (int) u->read_waiting);\n\n    /* ngx_shutdown_timer_handler will set c->close and c->error on timeout\n     * when worker_shutdown_timeout is configured.\n     * The rev->ready is false at that time, so we need to set u->eof.\n     */\n    if (c->close && c->error) {\n        u->eof = 1;\n    }\n\n    b = &u->buffer;\n    read = 0;\n\n    for ( ;; ) {\n\n        size = b->last - b->pos;\n\n        if (size || u->eof) {\n\n            rc = u->input_filter(u->input_filter_ctx, size);\n\n            if (rc == NGX_OK) {\n\n                ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"lua tcp socket receive done: wait:%d, eof:%d, \"\n                               \"uri:\\\"%V?%V\\\"\", (int) u->read_waiting,\n                               (int) u->eof, &r->uri, &r->args);\n\n                if (u->body_downstream\n                    && b->last == b->pos\n                    && r->request_body->rest == 0)\n                {\n\n                    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n                    if (llcf->check_client_abort) {\n                        rc = ngx_http_lua_check_broken_connection(r, rev);\n\n                        if (rc == NGX_OK) {\n                            goto success;\n                        }\n\n                        if (rc == NGX_HTTP_CLIENT_CLOSED_REQUEST) {\n                            ngx_http_lua_socket_handle_read_error(r, u,\n                                          NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT);\n\n                        } else {\n                            ngx_http_lua_socket_handle_read_error(r, u,\n                                             NGX_HTTP_LUA_SOCKET_FT_ERROR);\n                        }\n\n                        return NGX_ERROR;\n                    }\n                }\n\n#if 1\n                if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n                    ngx_http_lua_socket_handle_read_error(r, u,\n                                     NGX_HTTP_LUA_SOCKET_FT_ERROR);\n                    return NGX_ERROR;\n                }\n#endif\n\nsuccess:\n\n                ngx_http_lua_socket_handle_read_success(r, u);\n                return NGX_OK;\n            }\n\n            if (rc == NGX_ERROR) {\n                dd(\"input filter error: ft_type:%d wait:%d\",\n                   (int) u->ft_type, (int) u->read_waiting);\n\n                ngx_http_lua_socket_handle_read_error(r, u,\n                                                NGX_HTTP_LUA_SOCKET_FT_ERROR);\n                return NGX_ERROR;\n            }\n\n            /* rc == NGX_AGAIN */\n\n            if (u->body_downstream && r->request_body->rest == 0) {\n                u->eof = 1;\n            }\n\n            continue;\n        }\n\n        if (read && !rev->ready) {\n            rc = NGX_AGAIN;\n            break;\n        }\n\n        size = b->end - b->last;\n\n        if (size == 0) {\n            rc = ngx_http_lua_socket_add_input_buffer(r, u);\n            if (rc == NGX_ERROR) {\n                ngx_http_lua_socket_handle_read_error(r, u,\n                                                NGX_HTTP_LUA_SOCKET_FT_NOMEM);\n\n                return NGX_ERROR;\n            }\n\n            b = &u->buffer;\n            size = b->end - b->last;\n        }\n\n        if (u->raw_downstream) {\n            preread = r->header_in->last - r->header_in->pos;\n\n            if (preread) {\n\n                if (size > preread) {\n                    size = preread;\n                }\n\n                ngx_http_lua_probe_req_socket_consume_preread(r,\n                                                              r->header_in->pos,\n                                                              size);\n\n                b->last = ngx_copy(b->last, r->header_in->pos, size);\n                r->header_in->pos += size;\n                continue;\n            }\n\n        } else if (u->body_downstream) {\n\n            if (r->request_body->rest == 0) {\n\n                dd(\"request body rest is zero\");\n\n                u->eof = 1;\n\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"lua request body exhausted\");\n\n                continue;\n            }\n\n            /* try to process the preread body */\n\n            preread = r->header_in->last - r->header_in->pos;\n\n            if (preread) {\n\n                /* there is the pre-read part of the request body */\n\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"http client request body preread %O\", preread);\n\n                if (preread >= r->request_body->rest) {\n                    preread = r->request_body->rest;\n                }\n\n                if (size > preread) {\n                    size = preread;\n                }\n\n                ngx_http_lua_probe_req_socket_consume_preread(r,\n                                                              r->header_in->pos,\n                                                              size);\n\n                b->last = ngx_copy(b->last, r->header_in->pos, size);\n\n                r->header_in->pos += size;\n                r->request_length += size;\n\n                if (r->request_body->rest) {\n                    r->request_body->rest -= size;\n                }\n\n                continue;\n            }\n\n            if (size > r->request_body->rest) {\n                size = r->request_body->rest;\n            }\n        }\n\n#if 1\n        if (rev->active && !rev->ready) {\n            rc = NGX_AGAIN;\n            break;\n        }\n#endif\n\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket try to recv data %O: \\\"%V?%V\\\"\",\n                       size, &r->uri, &r->args);\n\n        n = c->recv(c, b->last, size);\n\n        dd(\"read event ready: %d\", (int) c->read->ready);\n\n        read = 1;\n\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket recv returned %d: \\\"%V?%V\\\"\",\n                       (int) n, &r->uri, &r->args);\n\n        if (n == NGX_AGAIN) {\n            rc = NGX_AGAIN;\n            dd(\"socket recv busy\");\n            break;\n        }\n\n        if (n == 0) {\n\n            if (u->raw_downstream || u->body_downstream) {\n\n                llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n                if (llcf->check_client_abort) {\n\n                    ngx_http_lua_socket_handle_read_error(r, u,\n                                          NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT);\n                    return NGX_ERROR;\n                }\n\n                /* llcf->check_client_abort == 0 */\n\n                if (u->body_downstream && r->request_body->rest) {\n                    ngx_http_lua_socket_handle_read_error(r, u,\n                                          NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT);\n                    return NGX_ERROR;\n                }\n            }\n\n            u->eof = 1;\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"lua tcp socket closed\");\n\n            continue;\n        }\n\n        if (n == NGX_ERROR) {\n            u->socket_errno = ngx_socket_errno;\n            ngx_http_lua_socket_handle_read_error(r, u,\n                                                  NGX_HTTP_LUA_SOCKET_FT_ERROR);\n            return NGX_ERROR;\n        }\n\n        b->last += n;\n\n        if (u->body_downstream) {\n            r->request_length += n;\n            r->request_body->rest -= n;\n        }\n    }\n\n#if 1\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        ngx_http_lua_socket_handle_read_error(r, u,\n                                              NGX_HTTP_LUA_SOCKET_FT_ERROR);\n        return NGX_ERROR;\n    }\n#endif\n\n    if (rev->active) {\n        ngx_add_timer(rev, u->read_timeout);\n\n    } else if (rev->timer_set) {\n        ngx_del_timer(rev);\n    }\n\n    return rc;\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_send(lua_State *L)\n{\n    ngx_int_t                            rc;\n    ngx_http_request_t                  *r;\n    u_char                              *p;\n    size_t                               len;\n    ngx_chain_t                         *cl;\n    ngx_http_lua_ctx_t                  *ctx;\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n    int                                  type;\n    int                                  tcp_nodelay;\n    const char                          *msg;\n    ngx_buf_t                           *b;\n    ngx_connection_t                    *c;\n    ngx_http_lua_loc_conf_t             *llcf;\n    ngx_http_core_loc_conf_t            *clcf;\n    ngx_http_lua_co_ctx_t               *coctx;\n\n    /* TODO: add support for the optional \"i\" and \"j\" arguments */\n\n    if (lua_gettop(L) != 2) {\n        return luaL_error(L, \"expecting 2 arguments (including the object), \"\n                          \"but got %d\", lua_gettop(L));\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    dd(\"tcp send: u=%p, u->write_closed=%d\", u, (unsigned) u->write_closed);\n\n    if (u == NULL || u->peer.connection == NULL || u->write_closed) {\n        llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n        if (llcf->log_socket_errors) {\n            ngx_log_t  *log;\n\n            log = r->connection->log;\n            if (u != NULL && u->peer.connection != NULL) {\n                log = u->peer.log;\n            }\n\n            ngx_log_error(NGX_LOG_ERR, log, 0,\n                          \"attempt to send data on a closed socket: u:%p, \"\n                          \"c:%p, ft:%d eof:%d\",\n                          u, u ? u->peer.connection : NULL,\n                          u ? (int) u->ft_type : 0, u ? (int) u->eof : 0);\n        }\n\n        lua_pushnil(L);\n        lua_pushliteral(L, \"closed\");\n        return 2;\n    }\n\n    if (u->request != r) {\n        return luaL_error(L, \"bad request\");\n    }\n\n    ngx_http_lua_socket_check_busy_connecting(r, u, L);\n    ngx_http_lua_socket_check_busy_writing(r, u, L);\n\n    if (u->body_downstream) {\n        return luaL_error(L, \"attempt to write to request sockets\");\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket send timeout: %M\", u->send_timeout);\n\n    type = lua_type(L, 2);\n    switch (type) {\n        case LUA_TNUMBER:\n            len = ngx_http_lua_get_num_len(L, 2);\n            break;\n\n        case LUA_TSTRING:\n            lua_tolstring(L, 2, &len);\n            break;\n\n        case LUA_TTABLE:\n            /* The maximum possible length, not the actual length */\n            len = ngx_http_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */);\n            break;\n\n        case LUA_TNIL:\n            len = sizeof(\"nil\") - 1;\n            break;\n\n        case LUA_TBOOLEAN:\n            if (lua_toboolean(L, 2)) {\n                len = sizeof(\"true\") - 1;\n\n            } else {\n                len = sizeof(\"false\") - 1;\n            }\n\n            break;\n\n        default:\n            msg = lua_pushfstring(L, \"string, number, boolean, nil, \"\n                                  \"or array table expected, got %s\",\n                                  lua_typename(L, type));\n\n            return luaL_argerror(L, 2, msg);\n    }\n\n    if (len == 0) {\n        lua_pushinteger(L, 0);\n        return 1;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,\n                                         &ctx->free_bufs, len);\n\n    if (cl == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    b = cl->buf;\n\n    switch (type) {\n        case LUA_TNUMBER:\n            b->last = ngx_http_lua_write_num(L, 2, b->last);\n            break;\n\n        case LUA_TSTRING:\n            p = (u_char *) lua_tolstring(L, 2, &len);\n            b->last = ngx_copy(b->last, (u_char *) p, len);\n            break;\n\n        case LUA_TTABLE:\n            b->last = ngx_http_lua_copy_str_in_table(L, 2, b->last);\n            break;\n\n        case LUA_TNIL:\n            *b->last++ = 'n';\n            *b->last++ = 'i';\n            *b->last++ = 'l';\n            break;\n\n        case LUA_TBOOLEAN:\n            if (lua_toboolean(L, 2)) {\n                *b->last++ = 't';\n                *b->last++ = 'r';\n                *b->last++ = 'u';\n                *b->last++ = 'e';\n\n            } else {\n                *b->last++ = 'f';\n                *b->last++ = 'a';\n                *b->last++ = 'l';\n                *b->last++ = 's';\n                *b->last++ = 'e';\n            }\n\n            break;\n\n        default:\n            return luaL_error(L, \"impossible to reach here\");\n    }\n\n    u->request_bufs = cl;\n\n    lua_assert(b->last - b->start <= len);\n\n    len = b->last - b->start;\n\n    u->request_len = len;\n\n    /* mimic ngx_http_upstream_init_request here */\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    c = u->peer.connection;\n\n    if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"lua socket tcp_nodelay\");\n\n        tcp_nodelay = 1;\n\n        if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,\n                       (const void *) &tcp_nodelay, sizeof(int))\n            == -1)\n        {\n            llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n            if (llcf->log_socket_errors) {\n                ngx_connection_error(c, ngx_socket_errno,\n                                     \"setsockopt(TCP_NODELAY) \"\n                                     \"failed\");\n            }\n\n            lua_pushnil(L);\n            lua_pushliteral(L, \"setsocketopt tcp_nodelay failed\");\n            return 2;\n        }\n\n        c->tcp_nodelay = NGX_TCP_NODELAY_SET;\n    }\n\n#if 1\n    u->write_waiting = 0;\n    u->write_co_ctx = NULL;\n#endif\n\n    ngx_http_lua_probe_socket_tcp_send_start(r, u, b->pos, len);\n\n    rc = ngx_http_lua_socket_send(r, u);\n\n    dd(\"socket send returned %d\", (int) rc);\n\n    if (rc == NGX_ERROR) {\n        return ngx_http_lua_socket_write_error_retval_handler(r, u, L);\n    }\n\n    if (rc == NGX_OK) {\n        lua_pushinteger(L, len);\n        return 1;\n    }\n\n    /* rc == NGX_AGAIN */\n\n    coctx = ctx->cur_co_ctx;\n\n    ngx_http_lua_cleanup_pending_operation(coctx);\n    coctx->cleanup = ngx_http_lua_coctx_cleanup;\n    coctx->data = u;\n\n    if (u->raw_downstream) {\n        ctx->writing_raw_req_socket = 1;\n    }\n\n    if (ctx->entered_content_phase) {\n        r->write_event_handler = ngx_http_lua_content_wev_handler;\n\n    } else {\n        r->write_event_handler = ngx_http_core_run_phases;\n    }\n\n    u->write_co_ctx = coctx;\n    u->write_waiting = 1;\n    u->write_prepare_retvals = ngx_http_lua_socket_tcp_send_retval_handler;\n\n    dd(\"setting data to %p\", u);\n\n    return lua_yield(L, 0);\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_send_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket send return value handler\");\n\n    if (u->ft_type) {\n        return ngx_http_lua_socket_write_error_retval_handler(r, u, L);\n    }\n\n    lua_pushinteger(L, u->request_len);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_receive_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L)\n{\n    int                          n;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_event_t                 *ev;\n\n    ngx_http_lua_loc_conf_t             *llcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket receive return value handler\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n#if 1\n    if (u->raw_downstream || u->body_downstream) {\n        llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n        if (llcf->check_client_abort) {\n\n            r->read_event_handler = ngx_http_lua_rd_check_broken_connection;\n\n            ev = r->connection->read;\n\n            dd(\"rev active: %d\", ev->active);\n\n            if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && !ev->active) {\n                if (ngx_add_event(ev, NGX_READ_EVENT, 0) != NGX_OK) {\n                    lua_pushnil(L);\n                    lua_pushliteral(L, \"failed to add event\");\n                    return 2;\n                }\n            }\n\n        } else {\n            /* llcf->check_client_abort == 0 */\n            r->read_event_handler = ngx_http_block_reading;\n        }\n    }\n#endif\n\n    if (u->ft_type) {\n\n        if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_TIMEOUT) {\n            u->no_close = 1;\n        }\n\n        dd(\"u->bufs_in: %p\", u->bufs_in);\n\n        if (u->bufs_in) {\n            ngx_http_lua_socket_push_input_data(r, ctx, u, L);\n\n            (void) ngx_http_lua_socket_read_error_retval_handler(r, u, L);\n\n            lua_pushvalue(L, -3);\n            lua_remove(L, -4);\n            return 3;\n        }\n\n        n = ngx_http_lua_socket_read_error_retval_handler(r, u, L);\n        lua_pushliteral(L, \"\");\n        return n + 1;\n    }\n\n    ngx_http_lua_socket_push_input_data(r, ctx, u, L);\n\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_close(lua_State *L)\n{\n    ngx_http_request_t                  *r;\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n\n    if (lua_gettop(L) != 1) {\n        return luaL_error(L, \"expecting 1 argument \"\n                          \"(including the object) but seen %d\", lua_gettop(L));\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    if (u == NULL\n        || u->peer.connection == NULL\n        || (u->read_closed && u->write_closed))\n    {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"closed\");\n        return 2;\n    }\n\n    if (u->request != r) {\n        return luaL_error(L, \"bad request\");\n    }\n\n    ngx_http_lua_socket_check_busy_connecting(r, u, L);\n    ngx_http_lua_socket_check_busy_reading(r, u, L);\n    ngx_http_lua_socket_check_busy_writing(r, u, L);\n\n    if (u->raw_downstream || u->body_downstream) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"attempt to close a request socket\");\n        return 2;\n    }\n\n    ngx_http_lua_socket_tcp_finalize(r, u);\n\n    lua_pushinteger(L, 1);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_settimeout(lua_State *L)\n{\n    int                     n;\n    ngx_int_t               timeout;\n\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n\n    n = lua_gettop(L);\n\n    if (n != 2) {\n        return luaL_error(L, \"ngx.socket settimeout: expecting 2 arguments \"\n                          \"(including the object) but seen %d\", lua_gettop(L));\n    }\n\n    timeout = (ngx_int_t) lua_tonumber(L, 2);\n    if (timeout >> 31) {\n        return luaL_error(L, \"bad timeout value\");\n    }\n\n    lua_pushinteger(L, timeout);\n    lua_pushinteger(L, timeout);\n\n    lua_rawseti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX);\n    lua_rawseti(L, 1, SOCKET_SEND_TIMEOUT_INDEX);\n    lua_rawseti(L, 1, SOCKET_READ_TIMEOUT_INDEX);\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n\n    if (u) {\n        if (timeout > 0) {\n            u->read_timeout = (ngx_msec_t) timeout;\n            u->send_timeout = (ngx_msec_t) timeout;\n            u->connect_timeout = (ngx_msec_t) timeout;\n\n        } else {\n            u->read_timeout = u->conf->read_timeout;\n            u->send_timeout = u->conf->send_timeout;\n            u->connect_timeout = u->conf->connect_timeout;\n        }\n    }\n\n    return 0;\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_settimeouts(lua_State *L)\n{\n    int                     n;\n    ngx_int_t               connect_timeout, send_timeout, read_timeout;\n\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n\n    n = lua_gettop(L);\n\n    if (n != 4) {\n        return luaL_error(L, \"ngx.socket settimeouts: expecting 4 arguments \"\n                          \"(including the object) but seen %d\", lua_gettop(L));\n    }\n\n    connect_timeout = (ngx_int_t) lua_tonumber(L, 2);\n    if (connect_timeout >> 31) {\n        return luaL_error(L, \"bad timeout value\");\n    }\n\n    send_timeout = (ngx_int_t) lua_tonumber(L, 3);\n    if (send_timeout >> 31) {\n        return luaL_error(L, \"bad timeout value\");\n    }\n\n    read_timeout = (ngx_int_t) lua_tonumber(L, 4);\n    if (read_timeout >> 31) {\n        return luaL_error(L, \"bad timeout value\");\n    }\n\n    lua_rawseti(L, 1, SOCKET_READ_TIMEOUT_INDEX);\n    lua_rawseti(L, 1, SOCKET_SEND_TIMEOUT_INDEX);\n    lua_rawseti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX);\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n\n    if (u) {\n        if (connect_timeout > 0) {\n            u->connect_timeout = (ngx_msec_t) connect_timeout;\n\n        } else {\n            u->connect_timeout = u->conf->connect_timeout;\n        }\n\n        if (send_timeout > 0) {\n            u->send_timeout = (ngx_msec_t) send_timeout;\n\n        } else {\n            u->send_timeout = u->conf->send_timeout;\n        }\n\n        if (read_timeout > 0) {\n            u->read_timeout = (ngx_msec_t) read_timeout;\n\n        } else {\n            u->read_timeout = u->conf->read_timeout;\n        }\n    }\n\n    return 0;\n}\n\n\nstatic void\nngx_http_lua_socket_tcp_handler(ngx_event_t *ev)\n{\n    ngx_connection_t                *c;\n    ngx_http_request_t              *r;\n    ngx_http_log_ctx_t              *ctx;\n\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n\n    c = ev->data;\n    u = c->data;\n    r = u->request;\n    c = r->connection;\n\n    if (c->fd != (ngx_socket_t) -1) {  /* not a fake connection */\n        ctx = c->log->data;\n        ctx->current_request = r;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"lua tcp socket handler for \\\"%V?%V\\\", wev %d\", &r->uri,\n                   &r->args, (int) ev->write);\n\n    if (ev->write) {\n        u->write_event_handler(r, u);\n\n    } else {\n        u->read_event_handler(r, u);\n    }\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc, void *data)\n{\n    pc->type = SOCK_STREAM;\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_lua_socket_read_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_connection_t            *c;\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    c = u->peer.connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket read handler\");\n\n    if (c->read->timedout) {\n        c->read->timedout = 0;\n\n        llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n        if (llcf->log_socket_errors) {\n            ngx_log_error(NGX_LOG_ERR, u->peer.log, 0,\n                          \"lua tcp socket read timed out\");\n        }\n\n        ngx_http_lua_socket_handle_read_error(r, u,\n                                              NGX_HTTP_LUA_SOCKET_FT_TIMEOUT);\n        return;\n    }\n\n#if 1\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n#endif\n\n    if (u->buffer.start != NULL) {\n        (void) ngx_http_lua_socket_tcp_read(r, u);\n    }\n}\n\n\nstatic void\nngx_http_lua_socket_send_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_connection_t            *c;\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    c = u->peer.connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket send handler\");\n\n    if (c->write->timedout) {\n        llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n        if (llcf->log_socket_errors) {\n            ngx_log_error(NGX_LOG_ERR, u->peer.log, 0,\n                          \"lua tcp socket write timed out\");\n        }\n\n        ngx_http_lua_socket_handle_write_error(r, u,\n                                               NGX_HTTP_LUA_SOCKET_FT_TIMEOUT);\n        return;\n    }\n\n    if (u->request_bufs) {\n        (void) ngx_http_lua_socket_send(r, u);\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_send(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_int_t                    n;\n    ngx_connection_t            *c;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_buf_t                   *b;\n\n    c = u->peer.connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket send data\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        ngx_http_lua_socket_handle_write_error(r, u,\n                                               NGX_HTTP_LUA_SOCKET_FT_ERROR);\n        return NGX_ERROR;\n    }\n\n    b = u->request_bufs->buf;\n\n    for (;;) {\n        n = c->send(c, b->pos, b->last - b->pos);\n\n        if (n >= 0) {\n            b->pos += n;\n\n            if (b->pos == b->last) {\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                               \"lua tcp socket sent all the data\");\n\n                if (c->write->timer_set) {\n                    ngx_del_timer(c->write);\n                }\n\n\n                ngx_chain_update_chains(r->pool,\n                                        &ctx->free_bufs, &u->busy_bufs,\n                                        &u->request_bufs,\n                                        (ngx_buf_tag_t) &ngx_http_lua_module);\n\n                u->write_event_handler = ngx_http_lua_socket_dummy_handler;\n\n                if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n                    ngx_http_lua_socket_handle_write_error(r, u,\n                                                NGX_HTTP_LUA_SOCKET_FT_ERROR);\n                    return NGX_ERROR;\n                }\n\n                ngx_http_lua_socket_handle_write_success(r, u);\n                return NGX_OK;\n            }\n\n            /* keep sending more data */\n            continue;\n        }\n\n        /* NGX_ERROR || NGX_AGAIN */\n        break;\n    }\n\n    if (n == NGX_ERROR) {\n        c->error = 1;\n        u->socket_errno = ngx_socket_errno;\n        ngx_http_lua_socket_handle_write_error(r, u,\n                                               NGX_HTTP_LUA_SOCKET_FT_ERROR);\n        return NGX_ERROR;\n    }\n\n    /* n == NGX_AGAIN */\n\n    if (u->raw_downstream) {\n        ctx->writing_raw_req_socket = 1;\n    }\n\n    u->write_event_handler = ngx_http_lua_socket_send_handler;\n\n    ngx_add_timer(c->write, u->send_timeout);\n\n    if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) {\n        ngx_http_lua_socket_handle_write_error(r, u,\n                                               NGX_HTTP_LUA_SOCKET_FT_ERROR);\n        return NGX_ERROR;\n    }\n\n    return NGX_AGAIN;\n}\n\n\nstatic void\nngx_http_lua_socket_handle_conn_success(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n#if 1\n    u->read_event_handler = ngx_http_lua_socket_dummy_handler;\n    u->write_event_handler = ngx_http_lua_socket_dummy_handler;\n#endif\n\n    if (u->conn_waiting) {\n        u->conn_waiting = 0;\n\n        coctx = u->write_co_ctx;\n        coctx->cleanup = NULL;\n        u->write_co_ctx = NULL;\n\n        ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n        if (ctx == NULL) {\n            return;\n        }\n\n        ctx->resume_handler = ngx_http_lua_socket_tcp_conn_resume;\n        ctx->cur_co_ctx = coctx;\n\n        ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx)\n                            || coctx->co_ref >= 0));\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket waking up the current request (conn)\");\n\n        r->write_event_handler(r);\n    }\n}\n\n\nstatic void\nngx_http_lua_socket_handle_read_success(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n#if 1\n    u->read_event_handler = ngx_http_lua_socket_dummy_handler;\n#endif\n\n    if (u->read_waiting) {\n        u->read_waiting = 0;\n\n        coctx = u->read_co_ctx;\n        coctx->cleanup = NULL;\n        u->read_co_ctx = NULL;\n\n        ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n        if (ctx == NULL) {\n            return;\n        }\n\n        ctx->resume_handler = ngx_http_lua_socket_tcp_read_resume;\n        ctx->cur_co_ctx = coctx;\n\n        ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx)\n                            || coctx->co_ref >= 0));\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket waking up the current request (read)\");\n\n        r->write_event_handler(r);\n    }\n}\n\n\nstatic void\nngx_http_lua_socket_handle_write_success(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n#if 1\n    u->write_event_handler = ngx_http_lua_socket_dummy_handler;\n#endif\n\n    if (u->write_waiting) {\n        u->write_waiting = 0;\n\n        coctx = u->write_co_ctx;\n        coctx->cleanup = NULL;\n        u->write_co_ctx = NULL;\n\n        ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n        if (ctx == NULL) {\n            return;\n        }\n\n        ctx->resume_handler = ngx_http_lua_socket_tcp_write_resume;\n        ctx->cur_co_ctx = coctx;\n\n        ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx)\n                            || coctx->co_ref >= 0));\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket waking up the current request (read)\");\n\n        r->write_event_handler(r);\n    }\n}\n\n\nstatic void\nngx_http_lua_socket_handle_conn_error(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type)\n{\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket handle connect error\");\n\n    u->ft_type |= ft_type;\n\n#if 1\n    ngx_http_lua_socket_tcp_finalize(r, u);\n#endif\n\n    u->read_event_handler = ngx_http_lua_socket_dummy_handler;\n    u->write_event_handler = ngx_http_lua_socket_dummy_handler;\n\n    dd(\"connection waiting: %d\", (int) u->conn_waiting);\n\n    coctx = u->write_co_ctx;\n\n    if (u->conn_waiting) {\n        u->conn_waiting = 0;\n\n        coctx->cleanup = NULL;\n        u->write_co_ctx = NULL;\n\n        ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n        ctx->resume_handler = ngx_http_lua_socket_tcp_conn_resume;\n        ctx->cur_co_ctx = coctx;\n\n        ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx)\n                            || coctx->co_ref >= 0));\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket waking up the current request\");\n\n        r->write_event_handler(r);\n    }\n}\n\n\nstatic void\nngx_http_lua_socket_handle_read_error(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type)\n{\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket handle read error\");\n\n    u->ft_type |= ft_type;\n\n#if 0\n    ngx_http_lua_socket_tcp_finalize(r, u);\n#endif\n\n    u->read_event_handler = ngx_http_lua_socket_dummy_handler;\n\n    if (u->read_waiting) {\n        u->read_waiting = 0;\n\n        coctx = u->read_co_ctx;\n        coctx->cleanup = NULL;\n        u->read_co_ctx = NULL;\n\n        ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n        ctx->resume_handler = ngx_http_lua_socket_tcp_read_resume;\n        ctx->cur_co_ctx = coctx;\n\n        ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx)\n                            || coctx->co_ref >= 0));\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket waking up the current request\");\n\n        r->write_event_handler(r);\n    }\n}\n\n\nstatic void\nngx_http_lua_socket_handle_write_error(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type)\n{\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket handle write error\");\n\n    u->ft_type |= ft_type;\n\n#if 0\n    ngx_http_lua_socket_tcp_finalize(r, u);\n#endif\n\n    u->write_event_handler = ngx_http_lua_socket_dummy_handler;\n\n    if (u->write_waiting) {\n        u->write_waiting = 0;\n\n        coctx = u->write_co_ctx;\n        coctx->cleanup = NULL;\n        u->write_co_ctx = NULL;\n\n        ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n        ctx->resume_handler = ngx_http_lua_socket_tcp_write_resume;\n        ctx->cur_co_ctx = coctx;\n\n        ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx)\n                            || coctx->co_ref >= 0));\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket waking up the current request\");\n\n        r->write_event_handler(r);\n    }\n}\n\n\nstatic void\nngx_http_lua_socket_connected_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_int_t                    rc;\n    ngx_connection_t            *c;\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    c = u->peer.connection;\n\n    if (c->write->timedout) {\n\n        llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n        if (llcf->log_socket_errors) {\n            ngx_http_lua_socket_init_peer_connection_addr_text(&u->peer);\n            ngx_log_error(NGX_LOG_ERR, u->peer.log, 0,\n                          \"lua tcp socket connect timed out\");\n        }\n\n        ngx_http_lua_socket_handle_conn_error(r, u,\n                                              NGX_HTTP_LUA_SOCKET_FT_TIMEOUT);\n        return;\n    }\n\n    if (c->write->timer_set) {\n        ngx_del_timer(c->write);\n    }\n\n    rc = ngx_http_lua_socket_test_connect(r, c);\n    if (rc != NGX_OK) {\n        if (rc > 0) {\n            u->socket_errno = (ngx_err_t) rc;\n        }\n\n        ngx_http_lua_socket_handle_conn_error(r, u,\n                                              NGX_HTTP_LUA_SOCKET_FT_ERROR);\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket connected\");\n\n    /* We should delete the current write/read event\n     * here because the socket object may not be used immediately\n     * on the Lua land, thus causing hot spin around level triggered\n     * event poll and wasting CPU cycles. */\n\n    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n        ngx_http_lua_socket_handle_conn_error(r, u,\n                                              NGX_HTTP_LUA_SOCKET_FT_ERROR);\n        return;\n    }\n\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        ngx_http_lua_socket_handle_conn_error(r, u,\n                                              NGX_HTTP_LUA_SOCKET_FT_ERROR);\n        return;\n    }\n\n    ngx_http_lua_socket_handle_conn_success(r, u);\n}\n\n\nstatic void\nngx_http_lua_socket_tcp_cleanup(void *data)\n{\n    ngx_http_lua_socket_tcp_upstream_t  *u = data;\n\n    ngx_http_request_t  *r;\n\n    r = u->request;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"cleanup lua tcp socket request: \\\"%V\\\"\", &r->uri);\n\n    ngx_http_lua_socket_tcp_finalize(r, u);\n}\n\n\nstatic void\nngx_http_lua_socket_tcp_finalize_read_part(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_chain_t                         *cl;\n    ngx_chain_t                        **ll;\n    ngx_connection_t                    *c;\n    ngx_http_lua_ctx_t                  *ctx;\n\n    if (u->read_closed) {\n        return;\n    }\n\n    u->read_closed = 1;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (ctx && u->bufs_in) {\n\n        ll = &u->bufs_in;\n        for (cl = u->bufs_in; cl; cl = cl->next) {\n            dd(\"bufs_in chain: %p, next %p\", cl, cl->next);\n            cl->buf->pos = cl->buf->last;\n            ll = &cl->next;\n        }\n\n        dd(\"ctx: %p\", ctx);\n        dd(\"free recv bufs: %p\", ctx->free_recv_bufs);\n        *ll = ctx->free_recv_bufs;\n        ctx->free_recv_bufs = u->bufs_in;\n        u->bufs_in = NULL;\n        u->buf_in = NULL;\n        ngx_memzero(&u->buffer, sizeof(ngx_buf_t));\n    }\n\n    if (u->raw_downstream || u->body_downstream) {\n        if (r->connection->read->timer_set) {\n            ngx_del_timer(r->connection->read);\n        }\n        return;\n    }\n\n    c = u->peer.connection;\n\n    if (c) {\n        if (c->read->timer_set) {\n            ngx_del_timer(c->read);\n        }\n\n        if (c->read->active || c->read->disabled) {\n            ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);\n        }\n\n#if (nginx_version >= 1007005)\n        if (c->read->posted) {\n#else\n        if (c->read->prev) {\n#endif\n            ngx_delete_posted_event(c->read);\n        }\n\n        c->read->closed = 1;\n\n        /* TODO: shutdown the reading part of the connection */\n    }\n}\n\n\nstatic void\nngx_http_lua_socket_tcp_finalize_write_part(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_connection_t                    *c;\n    ngx_http_lua_ctx_t                  *ctx;\n\n    if (u->write_closed) {\n        return;\n    }\n\n    u->write_closed = 1;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (u->raw_downstream || u->body_downstream) {\n        if (ctx && ctx->writing_raw_req_socket) {\n            ctx->writing_raw_req_socket = 0;\n            if (r->connection->write->timer_set) {\n                ngx_del_timer(r->connection->write);\n            }\n\n            r->connection->write->error = 1;\n        }\n        return;\n    }\n\n    c = u->peer.connection;\n\n    if (c) {\n        if (c->write->timer_set) {\n            ngx_del_timer(c->write);\n        }\n\n        if (c->write->active || c->write->disabled) {\n            ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT);\n        }\n\n#if (nginx_version >= 1007005)\n        if (c->write->posted) {\n#else\n        if (c->write->prev) {\n#endif\n            ngx_delete_posted_event(c->write);\n        }\n\n        c->write->closed = 1;\n\n        /* TODO: shutdown the writing part of the connection */\n    }\n}\n\n\nstatic void\nngx_http_lua_socket_tcp_conn_op_timeout_handler(ngx_event_t *ev)\n{\n    ngx_http_lua_socket_tcp_upstream_t      *u;\n    ngx_http_lua_ctx_t                      *ctx;\n    ngx_connection_t                        *c;\n    ngx_http_request_t                      *r;\n    ngx_http_lua_co_ctx_t                   *coctx;\n    ngx_http_lua_loc_conf_t                 *llcf;\n    ngx_http_lua_socket_tcp_conn_op_ctx_t   *conn_op_ctx;\n\n    conn_op_ctx = ev->data;\n    ngx_queue_remove(&conn_op_ctx->queue);\n\n    u = conn_op_ctx->u;\n    r = u->request;\n\n    coctx = u->write_co_ctx;\n    coctx->cleanup = NULL;\n    /* note that we store conn_op_ctx in coctx->data instead of u */\n    coctx->data = conn_op_ctx;\n    u->write_co_ctx = NULL;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->log_socket_errors) {\n        ngx_log_error(NGX_LOG_ERR, u->peer.log, 0,\n                      \"lua tcp socket queued connect timed out,\"\n                      \" when trying to connect to %V:%ud\",\n                      &conn_op_ctx->host, conn_op_ctx->port);\n    }\n\n    ngx_queue_insert_head(&u->socket_pool->cache_connect_op,\n                          &conn_op_ctx->queue);\n    u->socket_pool->connections--;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return;\n    }\n\n    ctx->cur_co_ctx = coctx;\n\n    ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx)\n                        || coctx->co_ref >= 0));\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket waking up the current request\");\n\n    u->write_prepare_retvals =\n        ngx_http_lua_socket_tcp_conn_op_timeout_retval_handler;\n\n    c = r->connection;\n\n    if (ctx->entered_content_phase) {\n        (void) ngx_http_lua_socket_tcp_conn_op_resume(r);\n\n    } else {\n        ctx->resume_handler = ngx_http_lua_socket_tcp_conn_op_resume;\n        ngx_http_core_run_phases(r);\n    }\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_conn_op_timeout_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L)\n{\n    lua_pushnil(L);\n    lua_pushliteral(L, \"timeout\");\n    return 2;\n}\n\n\nstatic void\nngx_http_lua_socket_tcp_resume_conn_op(ngx_http_lua_socket_pool_t *spool)\n{\n    ngx_queue_t                             *q;\n    ngx_http_lua_socket_tcp_conn_op_ctx_t   *conn_op_ctx;\n\n#if (NGX_DEBUG)\n    ngx_http_lua_assert(spool->connections >= 0);\n\n#else\n    if (spool->connections < 0) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"lua tcp socket connections count mismatched for \"\n                      \"connection pool \\\"%s\\\", connections: %i, size: %i\",\n                      spool->key, spool->connections, spool->size);\n        spool->connections = 0;\n    }\n#endif\n\n    /* we manually destroy wait_connect_op before triggering connect\n     * operation resumption, so that there is no resumption happens when Nginx\n     * is exiting.\n     */\n    if (ngx_queue_empty(&spool->wait_connect_op)) {\n        return;\n    }\n\n    q = ngx_queue_head(&spool->wait_connect_op);\n    conn_op_ctx = ngx_queue_data(q, ngx_http_lua_socket_tcp_conn_op_ctx_t,\n                                 queue);\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua tcp socket post connect operation resumption \"\n                   \"u: %p, ctx: %p for connection pool \\\"%s\\\", \"\n                   \"connections: %i\",\n                   conn_op_ctx->u, conn_op_ctx, spool->key, spool->connections);\n\n    if (conn_op_ctx->event.timer_set) {\n        ngx_del_timer(&conn_op_ctx->event);\n    }\n\n    conn_op_ctx->event.handler =\n        ngx_http_lua_socket_tcp_conn_op_resume_handler;\n\n    ngx_post_event((&conn_op_ctx->event), &ngx_posted_events);\n}\n\n\nstatic void\nngx_http_lua_socket_tcp_conn_op_ctx_cleanup(void *data)\n{\n    ngx_http_lua_socket_tcp_upstream_t     *u;\n    ngx_http_lua_socket_tcp_conn_op_ctx_t  *conn_op_ctx = data;\n\n    u = conn_op_ctx->u;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0,\n                   \"cleanup lua tcp socket conn_op_ctx: %p, u: %p, \"\n                   \"request: \\\"%V\\\"\",\n                   conn_op_ctx, u, &u->request->uri);\n\n    ngx_queue_insert_head(&u->socket_pool->cache_connect_op,\n                          &conn_op_ctx->queue);\n}\n\n\nstatic void\nngx_http_lua_socket_tcp_conn_op_resume_handler(ngx_event_t *ev)\n{\n    ngx_queue_t                             *q;\n    ngx_connection_t                        *c;\n    ngx_http_lua_ctx_t                      *ctx;\n    ngx_http_request_t                      *r;\n    ngx_http_cleanup_t                      *cln;\n    ngx_http_lua_co_ctx_t                   *coctx;\n    ngx_http_lua_socket_pool_t              *spool;\n    ngx_http_lua_socket_tcp_upstream_t      *u;\n    ngx_http_lua_socket_tcp_conn_op_ctx_t   *conn_op_ctx;\n\n    conn_op_ctx = ev->data;\n    u = conn_op_ctx->u;\n    r = u->request;\n    spool = u->socket_pool;\n\n    if (ngx_queue_empty(&spool->wait_connect_op)) {\n#if (NGX_DEBUG)\n        ngx_http_lua_assert(!(spool->backlog >= 0\n                              && spool->connections > spool->size));\n\n#else\n        if (spool->backlog >= 0 && spool->connections > spool->size) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                          \"lua tcp socket connections count mismatched for \"\n                          \"connection pool \\\"%s\\\", connections: %i, size: %i\",\n                          spool->key, spool->connections, spool->size);\n            spool->connections = spool->size;\n        }\n#endif\n\n        return;\n    }\n\n    q = ngx_queue_head(&spool->wait_connect_op);\n    ngx_queue_remove(q);\n\n    coctx = u->write_co_ctx;\n    coctx->cleanup = NULL;\n    /* note that we store conn_op_ctx in coctx->data instead of u */\n    coctx->data = conn_op_ctx;\n    /* clear ngx_http_lua_tcp_queue_conn_op_cleanup */\n    u->write_co_ctx = NULL;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        ngx_queue_insert_head(&spool->cache_connect_op,\n                              &conn_op_ctx->queue);\n        return;\n    }\n\n    ctx->cur_co_ctx = coctx;\n\n    ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx)\n                        || coctx->co_ref >= 0));\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket waking up the current request\");\n\n    u->write_prepare_retvals =\n        ngx_http_lua_socket_tcp_conn_op_resume_retval_handler;\n\n    c = r->connection;\n\n    if (ctx->entered_content_phase) {\n        (void) ngx_http_lua_socket_tcp_conn_op_resume(r);\n\n    } else {\n        cln = ngx_http_lua_cleanup_add(r, 0);\n        if (cln != NULL) {\n            cln->handler = ngx_http_lua_socket_tcp_conn_op_ctx_cleanup;\n            cln->data = conn_op_ctx;\n            conn_op_ctx->cleanup = &cln->handler;\n        }\n\n        ctx->resume_handler = ngx_http_lua_socket_tcp_conn_op_resume;\n        ngx_http_core_run_phases(r);\n    }\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_conn_op_resume_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L)\n{\n    int                                      nret;\n    ngx_http_lua_ctx_t                      *ctx;\n    ngx_http_lua_co_ctx_t                   *coctx;\n    ngx_http_lua_socket_tcp_conn_op_ctx_t   *conn_op_ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    coctx = ctx->cur_co_ctx;\n    dd(\"coctx: %p\", coctx);\n    conn_op_ctx = coctx->data;\n    if (conn_op_ctx->cleanup != NULL) {\n        *conn_op_ctx->cleanup = NULL;\n        ngx_http_lua_cleanup_free(r, conn_op_ctx->cleanup);\n        conn_op_ctx->cleanup = NULL;\n    }\n\n    /* decrease pending connect operation counter */\n    u->socket_pool->connections--;\n\n    nret = ngx_http_lua_socket_tcp_connect_helper(L, u, r, ctx,\n                                                  conn_op_ctx->host.data,\n                                                  conn_op_ctx->host.len,\n                                                  conn_op_ctx->port, 1);\n    ngx_queue_insert_head(&u->socket_pool->cache_connect_op,\n                          &conn_op_ctx->queue);\n\n    return nret;\n}\n\n\nstatic void\nngx_http_lua_socket_tcp_finalize(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_connection_t               *c;\n    ngx_http_lua_socket_pool_t     *spool;\n\n    dd(\"request: %p, u: %p, u->cleanup: %p\", r, u, u->cleanup);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua finalize socket\");\n\n    if (u->cleanup) {\n        *u->cleanup = NULL;\n        ngx_http_lua_cleanup_free(r, u->cleanup);\n        u->cleanup = NULL;\n    }\n\n    ngx_http_lua_socket_tcp_finalize_read_part(r, u);\n    ngx_http_lua_socket_tcp_finalize_write_part(r, u);\n\n    if (u->input_filter_ctx != NULL && u->input_filter_ctx != u) {\n        ((ngx_http_lua_socket_compiled_pattern_t *)\n         u->input_filter_ctx)->upstream = NULL;\n    }\n\n    if (u->raw_downstream || u->body_downstream) {\n        u->peer.connection = NULL;\n        return;\n    }\n\n    if (u->resolved && u->resolved->ctx) {\n        ngx_resolve_name_done(u->resolved->ctx);\n        u->resolved->ctx = NULL;\n    }\n\n    if (u->peer.free) {\n        u->peer.free(&u->peer, u->peer.data, 0);\n    }\n\n#if (NGX_HTTP_SSL)\n    if (u->ssl_name.data) {\n        ngx_free(u->ssl_name.data);\n        u->ssl_name.data = NULL;\n        u->ssl_name.len = 0;\n    }\n#endif\n\n    c = u->peer.connection;\n    if (c) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua close socket connection\");\n\n        ngx_http_lua_socket_tcp_close_connection(c);\n        u->peer.connection = NULL;\n        u->conn_closed = 1;\n\n        spool = u->socket_pool;\n        if (spool == NULL) {\n            return;\n        }\n\n        spool->connections--;\n\n        if (spool->connections == 0) {\n            ngx_http_lua_socket_free_pool(r->connection->log, spool);\n            return;\n        }\n\n        ngx_http_lua_socket_tcp_resume_conn_op(spool);\n    }\n}\n\n\nstatic void\nngx_http_lua_socket_tcp_close_connection(ngx_connection_t *c)\n{\n#if (NGX_HTTP_SSL)\n\n    if (c->ssl) {\n        c->ssl->no_wait_shutdown = 1;\n        c->ssl->no_send_shutdown = 1;\n\n        (void) ngx_ssl_shutdown(c);\n    }\n\n#endif\n\n    if (c->pool) {\n        ngx_destroy_pool(c->pool);\n        c->pool = NULL;\n    }\n\n    ngx_close_connection(c);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_test_connect(ngx_http_request_t *r, ngx_connection_t *c)\n{\n    int              err;\n    socklen_t        len;\n\n    ngx_http_lua_loc_conf_t     *llcf;\n\n#if (NGX_HAVE_KQUEUE)\n\n    ngx_event_t     *ev;\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT)  {\n        dd(\"pending eof: (%p)%d (%p)%d\", c->write, c->write->pending_eof,\n           c->read, c->read->pending_eof);\n\n        if (c->write->pending_eof) {\n            ev = c->write;\n\n        } else if (c->read->pending_eof) {\n            ev = c->read;\n\n        } else {\n            ev = NULL;\n        }\n\n        if (ev) {\n            llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n            if (llcf->log_socket_errors) {\n                (void) ngx_connection_error(c, ev->kq_errno,\n                                            \"kevent() reported that \"\n                                            \"connect() failed\");\n            }\n            return ev->kq_errno;\n        }\n\n    } else\n#endif\n    {\n        err = 0;\n        len = sizeof(int);\n\n        /*\n         * BSDs and Linux return 0 and set a pending error in err\n         * Solaris returns -1 and sets errno\n         */\n\n        if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)\n            == -1)\n        {\n            err = ngx_errno;\n        }\n\n        if (err) {\n            llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n            if (llcf->log_socket_errors) {\n                (void) ngx_connection_error(c, err, \"connect() failed\");\n            }\n            return err;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_lua_socket_dummy_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket dummy handler\");\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_receiveuntil(lua_State *L)\n{\n    ngx_http_request_t                  *r;\n    int                                  n;\n    ngx_str_t                            pat;\n    ngx_int_t                            rc;\n    size_t                               size;\n    unsigned                             inclusive = 0;\n\n    ngx_http_lua_socket_compiled_pattern_t     *cp;\n\n    n = lua_gettop(L);\n    if (n != 2 && n != 3) {\n        return luaL_error(L, \"expecting 2 or 3 arguments \"\n                          \"(including the object), but got %d\", n);\n    }\n\n    if (n == 3) {\n        /* check out the options table */\n\n        luaL_checktype(L, 3, LUA_TTABLE);\n\n        lua_getfield(L, 3, \"inclusive\");\n\n        switch (lua_type(L, -1)) {\n            case LUA_TNIL:\n                /* do nothing */\n                break;\n\n            case LUA_TBOOLEAN:\n                if (lua_toboolean(L, -1)) {\n                    inclusive = 1;\n                }\n                break;\n\n            default:\n                return luaL_error(L, \"bad \\\"inclusive\\\" option value type: %s\",\n                                  luaL_typename(L, -1));\n\n        }\n\n        lua_pop(L, 2);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket calling receiveuntil() method\");\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len);\n    if (pat.len == 0) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"pattern is empty\");\n        return 2;\n    }\n\n    size = sizeof(ngx_http_lua_socket_compiled_pattern_t);\n\n    cp = lua_newuserdata(L, size);\n    if (cp == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          pattern_udata_metatable_key));\n    lua_rawget(L, LUA_REGISTRYINDEX);\n    lua_setmetatable(L, -2);\n\n    ngx_memzero(cp, size);\n\n    cp->inclusive = inclusive;\n\n    rc = ngx_http_lua_socket_compile_pattern(pat.data, pat.len, cp,\n                                             r->connection->log);\n\n    if (rc != NGX_OK) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"failed to compile pattern\");\n        return 2;\n    }\n\n    lua_pushcclosure(L, ngx_http_lua_socket_receiveuntil_iterator, 3);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_socket_receiveuntil_iterator(lua_State *L)\n{\n    ngx_http_request_t                  *r;\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n    ngx_int_t                            rc;\n    ngx_http_lua_ctx_t                  *ctx;\n    lua_Integer                          bytes;\n    int                                  n;\n    ngx_http_lua_co_ctx_t               *coctx;\n\n    ngx_http_lua_socket_compiled_pattern_t     *cp;\n\n    n = lua_gettop(L);\n    if (n > 1) {\n        return luaL_error(L, \"expecting 0 or 1 argument, \"\n                          \"but seen %d\", n);\n    }\n\n    if (n >= 1) {\n        bytes = luaL_checkinteger(L, 1);\n        if (bytes < 0) {\n            bytes = 0;\n        }\n\n    } else {\n        bytes = 0;\n    }\n\n    lua_rawgeti(L, lua_upvalueindex(1), SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    if (u == NULL || u->peer.connection == NULL || u->read_closed) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"closed\");\n        return 2;\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    if (u->request != r) {\n        return luaL_error(L, \"bad request\");\n    }\n\n    ngx_http_lua_socket_check_busy_connecting(r, u, L);\n    ngx_http_lua_socket_check_busy_reading(r, u, L);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket receiveuntil iterator\");\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket read timeout: %M\", u->read_timeout);\n\n    u->input_filter = ngx_http_lua_socket_read_until;\n\n    cp = lua_touserdata(L, lua_upvalueindex(3));\n\n    dd(\"checking existing state: %d\", cp->state);\n\n    if (cp->state == -1) {\n        cp->state = 0;\n\n        lua_pushnil(L);\n        lua_pushnil(L);\n        lua_pushnil(L);\n        return 3;\n    }\n\n    cp->upstream = u;\n\n    cp->pattern.data =\n        (u_char *) lua_tolstring(L, lua_upvalueindex(2),\n                                 &cp->pattern.len);\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (u->bufs_in == NULL) {\n        u->bufs_in =\n            ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,\n                                            &ctx->free_recv_bufs,\n                                            u->conf->buffer_size);\n\n        if (u->bufs_in == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        u->buf_in = u->bufs_in;\n        u->buffer = *u->buf_in->buf;\n    }\n\n    u->length = (size_t) bytes;\n    u->rest = u->length;\n\n    if (u->raw_downstream || u->body_downstream) {\n        r->read_event_handler = ngx_http_lua_req_socket_rev_handler;\n    }\n\n    u->read_waiting = 0;\n    u->read_co_ctx = NULL;\n\n    ngx_http_lua_socket_tcp_read_prepare(r, u, cp, L);\n\n    rc = ngx_http_lua_socket_tcp_read(r, u);\n\n    if (rc == NGX_ERROR) {\n        dd(\"read failed: %d\", (int) u->ft_type);\n        rc = ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L);\n        dd(\"tcp receive retval returned: %d\", (int) rc);\n        return rc;\n    }\n\n    if (rc == NGX_OK) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket receive done in a single run\");\n\n        return ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L);\n    }\n\n    /* rc == NGX_AGAIN */\n\n    coctx = ctx->cur_co_ctx;\n\n    u->read_event_handler = ngx_http_lua_socket_read_handler;\n\n    ngx_http_lua_cleanup_pending_operation(coctx);\n    coctx->cleanup = ngx_http_lua_coctx_cleanup;\n    coctx->data = u;\n\n    if (ctx->entered_content_phase) {\n        r->write_event_handler = ngx_http_lua_content_wev_handler;\n\n    } else {\n        r->write_event_handler = ngx_http_core_run_phases;\n    }\n\n    u->read_co_ctx = coctx;\n    u->read_waiting = 1;\n    u->read_prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler;\n\n    dd(\"setting data to %p\", u);\n\n    if (u->raw_downstream || u->body_downstream) {\n        ctx->downstream = u;\n    }\n\n    return lua_yield(L, 0);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_compile_pattern(u_char *data, size_t len,\n    ngx_http_lua_socket_compiled_pattern_t *cp, ngx_log_t *log)\n{\n    size_t              i;\n    size_t              prefix_len;\n    size_t              size;\n    unsigned            found;\n    int                 cur_state, new_state;\n\n    ngx_http_lua_dfa_edge_t         *edge;\n    ngx_http_lua_dfa_edge_t        **last = NULL;\n\n    cp->pattern.len = len;\n\n    if (len <= 2) {\n        return NGX_OK;\n    }\n\n    for (i = 1; i < len; i++) {\n        prefix_len = 1;\n\n        while (prefix_len <= len - i - 1) {\n\n            if (ngx_memcmp(data, &data[i], prefix_len) == 0) {\n                if (data[prefix_len] == data[i + prefix_len]) {\n                    prefix_len++;\n                    continue;\n                }\n\n                cur_state = i + prefix_len;\n                new_state = prefix_len + 1;\n\n                if (cp->recovering == NULL) {\n                    size = sizeof(void *) * (len - 2);\n                    cp->recovering = ngx_alloc(size, log);\n                    if (cp->recovering == NULL) {\n                        return NGX_ERROR;\n                    }\n\n                    ngx_memzero(cp->recovering, size);\n                }\n\n                edge = cp->recovering[cur_state - 2];\n\n                found = 0;\n\n                if (edge == NULL) {\n                    last = &cp->recovering[cur_state - 2];\n\n                } else {\n\n                    for (; edge; edge = edge->next) {\n                        last = &edge->next;\n\n                        if (edge->chr == data[prefix_len]) {\n                            found = 1;\n\n                            if (edge->new_state < new_state) {\n                                edge->new_state = new_state;\n                            }\n\n                            break;\n                        }\n                    }\n                }\n\n                if (!found) {\n                    ngx_log_debug7(NGX_LOG_DEBUG_HTTP, log, 0,\n                                   \"lua tcp socket read until recovering point:\"\n                                   \" on state %d (%*s), if next is '%c', then \"\n                                   \"recover to state %d (%*s)\", cur_state,\n                                   (size_t) cur_state, data, data[prefix_len],\n                                   new_state, (size_t) new_state, data);\n\n                    edge = ngx_alloc(sizeof(ngx_http_lua_dfa_edge_t), log);\n                    if (edge == NULL) {\n                        return NGX_ERROR;\n                    }\n\n                    edge->chr = data[prefix_len];\n                    edge->new_state = new_state;\n                    edge->next = NULL;\n\n                    *last = edge;\n                }\n\n                break;\n            }\n\n            break;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_read_until(void *data, ssize_t bytes)\n{\n    ngx_http_lua_socket_compiled_pattern_t     *cp = data;\n\n    ngx_http_lua_socket_tcp_upstream_t      *u;\n    ngx_http_request_t                      *r;\n    ngx_buf_t                               *b;\n    u_char                                   c;\n    u_char                                  *pat;\n    size_t                                   pat_len;\n    size_t                                   pending_len;\n    int                                      i;\n    int                                      state;\n    int                                      old_state = 0; /* just to make old\n                                                               gcc happy */\n    ngx_http_lua_dfa_edge_t                 *edge;\n    unsigned                                 matched;\n    ngx_int_t                                rc;\n\n    u = cp->upstream;\n    r = u->request;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket read until\");\n\n    if (bytes == 0) {\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED;\n        return NGX_ERROR;\n    }\n\n    b = &u->buffer;\n\n    pat = cp->pattern.data;\n    pat_len = cp->pattern.len;\n    state = cp->state;\n\n    i = 0;\n    while (i < bytes) {\n        c = b->pos[i];\n\n        dd(\"%d: read char %d, state: %d\", i, c, state);\n\n        if (c == pat[state]) {\n            i++;\n            state++;\n\n            if (state == (int) pat_len) {\n                /* already matched the whole pattern */\n                dd(\"pat len: %d\", (int) pat_len);\n\n                b->pos += i;\n\n                if (u->length) {\n                    cp->state = -1;\n\n                } else {\n                    cp->state = 0;\n                }\n\n                if (cp->inclusive) {\n                    rc = ngx_http_lua_socket_add_pending_data(r, u, b->pos, 0,\n                                                              pat, state,\n                                                              state);\n\n                    if (rc != NGX_OK) {\n                        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR;\n                        return NGX_ERROR;\n                    }\n                }\n\n                return NGX_OK;\n            }\n\n            continue;\n        }\n\n        if (state == 0) {\n            u->buf_in->buf->last++;\n\n            i++;\n\n            if (u->length && --u->rest == 0) {\n                cp->state = state;\n                b->pos += i;\n                return NGX_OK;\n            }\n\n            continue;\n        }\n\n        matched = 0;\n\n        if (cp->recovering && state >= 2) {\n            dd(\"accessing state: %d, index: %d\", state, state - 2);\n            for (edge = cp->recovering[state - 2]; edge; edge = edge->next) {\n\n                if (edge->chr == c) {\n                    dd(\"matched '%c' and jumping to state %d\", c,\n                       edge->new_state);\n\n                    old_state = state;\n                    state = edge->new_state;\n                    matched = 1;\n                    break;\n                }\n            }\n        }\n\n        if (!matched) {\n#if 1\n            dd(\"adding pending data: %.*s\", state, pat);\n            rc = ngx_http_lua_socket_add_pending_data(r, u, b->pos, i, pat,\n                                                      state, state);\n\n            if (rc != NGX_OK) {\n                u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR;\n                return NGX_ERROR;\n            }\n\n#endif\n\n            if (u->length) {\n                if (u->rest <= (size_t) state) {\n                    u->rest = 0;\n                    cp->state = 0;\n                    b->pos += i;\n                    return NGX_OK;\n\n                } else {\n                    u->rest -= state;\n                }\n            }\n\n            state = 0;\n            continue;\n        }\n\n        /* matched */\n\n        pending_len = old_state + 1 - state;\n\n        dd(\"adding pending data: %.*s\", (int) pending_len, (char *) pat);\n\n        rc = ngx_http_lua_socket_add_pending_data(r, u, b->pos, i, pat,\n                                                  pending_len,\n                                                  old_state);\n\n        if (rc != NGX_OK) {\n            u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR;\n            return NGX_ERROR;\n        }\n\n        i++;\n\n        if (u->length) {\n            if (u->rest <= pending_len) {\n                u->rest = 0;\n                cp->state = state;\n                b->pos += i;\n                return NGX_OK;\n\n            } else {\n                u->rest -= pending_len;\n            }\n        }\n\n        continue;\n    }\n\n    b->pos += i;\n    cp->state = state;\n\n    return NGX_AGAIN;\n}\n\n\nstatic int\nngx_http_lua_socket_cleanup_compiled_pattern(lua_State *L)\n{\n    ngx_http_lua_socket_compiled_pattern_t      *cp;\n\n    ngx_http_lua_socket_tcp_upstream_t      *u;\n    ngx_http_lua_dfa_edge_t                 *edge, *p;\n    unsigned                                 i;\n\n    dd(\"cleanup compiled pattern\");\n\n    cp = lua_touserdata(L, 1);\n    if (cp == NULL) {\n        return 0;\n    }\n\n    u = cp->upstream;\n    if (u != NULL) {\n        ngx_http_lua_socket_tcp_read_prepare(u->request, u, NULL, L);\n        u->input_filter_ctx = NULL;\n    }\n\n    if (cp->recovering == NULL) {\n        return 0;\n    }\n\n    dd(\"pattern len: %d\", (int) cp->pattern.len);\n\n    for (i = 0; i < cp->pattern.len - 2; i++) {\n        edge = cp->recovering[i];\n\n        while (edge) {\n            p = edge;\n            edge = edge->next;\n\n            dd(\"freeing edge %p\", p);\n\n            ngx_free(p);\n\n            dd(\"edge: %p\", edge);\n        }\n    }\n\n#if 1\n    ngx_free(cp->recovering);\n    cp->recovering = NULL;\n#endif\n\n    return 0;\n}\n\n\nstatic int\nngx_http_lua_req_socket(lua_State *L)\n{\n    int                              n, raw;\n    ngx_peer_connection_t           *pc;\n    ngx_http_lua_loc_conf_t         *llcf;\n    ngx_connection_t                *c;\n    ngx_http_request_t              *r;\n    ngx_http_lua_ctx_t              *ctx;\n    ngx_http_request_body_t         *rb;\n    ngx_http_cleanup_t              *cln;\n    ngx_http_lua_co_ctx_t           *coctx;\n\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n\n    n = lua_gettop(L);\n    if (n == 0) {\n        raw = 0;\n\n    } else if (n == 1) {\n        raw = lua_toboolean(L, 1);\n        lua_pop(L, 1);\n\n    } else {\n        return luaL_error(L, \"expecting 0 or 1 argument, but got %d\",\n                          lua_gettop(L));\n    }\n\n    r = ngx_http_lua_get_req(L);\n\n    if (r != r->main) {\n        return luaL_error(L, \"attempt to read the request body in a \"\n                          \"subrequest\");\n    }\n\n#if (NGX_HTTP_SPDY)\n    if (r->spdy_stream) {\n        return luaL_error(L, \"spdy not supported yet\");\n    }\n#endif\n\n#if (NGX_HTTP_V2)\n    if (r->stream) {\n        return luaL_error(L, \"http v2 not supported yet\");\n    }\n#endif\n\n#if (NGX_HTTP_V3)\n    if (r->http_version == NGX_HTTP_VERSION_30) {\n        return luaL_error(L, \"http v3 not supported yet\");\n    }\n#endif\n\n    if (!raw && r->headers_in.chunked) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"chunked request bodies not supported yet\");\n        return 2;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_ACCESS\n                               | NGX_HTTP_LUA_CONTEXT_PRECONTENT\n                               | NGX_HTTP_LUA_CONTEXT_CONTENT);\n\n    c = r->connection;\n\n    if (raw) {\n        if (r->request_body) {\n            if (r->request_body->rest > 0) {\n                lua_pushnil(L);\n                lua_pushliteral(L, \"pending request body reading in some \"\n                                \"other thread\");\n                return 2;\n            }\n\n        } else {\n            rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));\n            if (rb == NULL) {\n                return luaL_error(L, \"no memory\");\n            }\n\n            r->request_body = rb;\n        }\n\n        if (c->buffered & NGX_HTTP_LOWLEVEL_BUFFERED) {\n            lua_pushnil(L);\n            lua_pushliteral(L, \"pending data to write\");\n            return 2;\n        }\n\n        if (ctx->buffering) {\n            lua_pushnil(L);\n            lua_pushliteral(L, \"http 1.0 buffering\");\n            return 2;\n        }\n\n        if (!r->header_sent) {\n            /* prevent other parts of nginx from sending out\n             * the response header */\n            r->header_sent = 1;\n        }\n\n        ctx->header_sent = 1;\n\n        dd(\"ctx acquired raw req socket: %d\", ctx->acquired_raw_req_socket);\n\n        if (ctx->acquired_raw_req_socket) {\n            lua_pushnil(L);\n            lua_pushliteral(L, \"duplicate call\");\n            return 2;\n        }\n\n        ctx->acquired_raw_req_socket = 1;\n        r->keepalive = 0;\n        r->lingering_close = 1;\n\n    } else {\n        /* request body reader */\n\n        if (r->request_body) {\n            lua_pushnil(L);\n            lua_pushliteral(L, \"request body already exists\");\n            return 2;\n        }\n\n        if (r->discard_body) {\n            lua_pushnil(L);\n            lua_pushliteral(L, \"request body discarded\");\n            return 2;\n        }\n\n        dd(\"req content length: %d\", (int) r->headers_in.content_length_n);\n\n        if (r->headers_in.content_length_n <= 0) {\n            lua_pushnil(L);\n            lua_pushliteral(L, \"no body\");\n            return 2;\n        }\n\n        if (ngx_http_lua_test_expect(r) != NGX_OK) {\n            lua_pushnil(L);\n            lua_pushliteral(L, \"test expect failed\");\n            return 2;\n        }\n\n        /* prevent other request body reader from running */\n\n        rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));\n        if (rb == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        rb->rest = r->headers_in.content_length_n;\n\n        r->request_body = rb;\n    }\n\n    lua_createtable(L, 2 /* narr */, 3 /* nrec */); /* the object */\n\n    if (raw) {\n        lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                              raw_req_socket_metatable_key));\n\n    } else {\n        lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                              req_socket_metatable_key));\n    }\n\n    lua_rawget(L, LUA_REGISTRYINDEX);\n    lua_setmetatable(L, -2);\n\n    u = lua_newuserdata(L, sizeof(ngx_http_lua_socket_tcp_upstream_t));\n    if (u == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n#if 1\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          downstream_udata_metatable_key));\n    lua_rawget(L, LUA_REGISTRYINDEX);\n    lua_setmetatable(L, -2);\n#endif\n\n    lua_rawseti(L, 1, SOCKET_CTX_INDEX);\n\n    ngx_memzero(u, sizeof(ngx_http_lua_socket_tcp_upstream_t));\n\n    if (raw) {\n        u->raw_downstream = 1;\n\n    } else {\n        u->body_downstream = 1;\n    }\n\n    coctx = ctx->cur_co_ctx;\n\n    u->request = r;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    u->conf = llcf;\n\n    u->read_timeout = u->conf->read_timeout;\n    u->connect_timeout = u->conf->connect_timeout;\n    u->send_timeout = u->conf->send_timeout;\n\n    cln = ngx_http_lua_cleanup_add(r, 0);\n    if (cln == NULL) {\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR;\n        lua_pushnil(L);\n        lua_pushliteral(L, \"no memory\");\n        return 2;\n    }\n\n    cln->handler = ngx_http_lua_socket_tcp_cleanup;\n    cln->data = u;\n    u->cleanup = &cln->handler;\n\n    pc = &u->peer;\n\n    pc->log = c->log;\n    pc->log_error = NGX_ERROR_ERR;\n\n    pc->connection = c;\n\n    dd(\"setting data to %p\", u);\n\n    coctx->data = u;\n    ctx->downstream = u;\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    if (raw) {\n        if (c->write->timer_set) {\n            ngx_del_timer(c->write);\n        }\n    }\n\n    lua_settop(L, 1);\n    return 1;\n}\n\n\nstatic void\nngx_http_lua_req_socket_rev_handler(ngx_http_request_t *r)\n{\n    ngx_http_lua_ctx_t                  *ctx;\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua request socket read event handler\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        r->read_event_handler = ngx_http_block_reading;\n        return;\n    }\n\n    u = ctx->downstream;\n    if (u == NULL || u->peer.connection == NULL) {\n        r->read_event_handler = ngx_http_block_reading;\n        return;\n    }\n\n    u->read_event_handler(r, u);\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_getreusedtimes(lua_State *L)\n{\n    ngx_http_lua_socket_tcp_upstream_t    *u;\n\n    if (lua_gettop(L) != 1) {\n        return luaL_error(L, \"expecting 1 argument \"\n                          \"(including the object), but got %d\", lua_gettop(L));\n    }\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n\n    if (u == NULL\n        || u->peer.connection == NULL\n        || (u->read_closed && u->write_closed))\n    {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"closed\");\n        return 2;\n    }\n\n    lua_pushinteger(L, u->reused);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_setkeepalive(lua_State *L)\n{\n    ngx_http_lua_loc_conf_t             *llcf;\n    ngx_http_lua_socket_tcp_upstream_t  *u;\n    ngx_connection_t                    *c;\n    ngx_http_lua_socket_pool_t          *spool;\n    ngx_str_t                            key;\n    ngx_queue_t                         *q;\n    ngx_peer_connection_t               *pc;\n    ngx_http_request_t                  *r;\n    ngx_msec_t                           timeout;\n    ngx_int_t                            pool_size;\n    int                                  n;\n    ngx_int_t                            rc;\n    ngx_buf_t                           *b;\n    const char                          *msg;\n\n    ngx_http_lua_socket_pool_item_t     *item;\n\n    n = lua_gettop(L);\n\n    if (n < 1 || n > 3) {\n        return luaL_error(L, \"expecting 1 to 3 arguments \"\n                          \"(including the object), but got %d\", n);\n    }\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    /* luaL_checkinteger will throw error if the argument is not a number.\n     * e.g.: bad argument \\#2 to '?' (number expected, got string)\n     *\n     * We should check the argument in advance; otherwise,\n     * throwing an exception in the middle can compromise data integrity.\n     * e.g.: set pc->connection to NULL without following cleanup.\n     */\n    if (n >= 2 && !lua_isnil(L, 2)) {\n        timeout = (ngx_msec_t) luaL_checkinteger(L, 2);\n\n    } else {\n        timeout = llcf->keepalive_timeout;\n    }\n\n    if (n >= 3 && !lua_isnil(L, 3)) {\n        pool_size = luaL_checkinteger(L, 3);\n\n    } else {\n        pool_size = llcf->pool_size;\n    }\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    if (u == NULL) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"closed\");\n        return 2;\n    }\n\n    /* stack: obj timeout? size? */\n\n    pc = &u->peer;\n    c = pc->connection;\n\n    /* When the server closes the connection,\n     * epoll will return EPOLLRDHUP event and nginx will set pending_eof.\n     */\n    if (c == NULL || u->read_closed || u->write_closed\n        || c->read->eof || c->read->pending_eof)\n    {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"closed\");\n        return 2;\n    }\n\n    if (u->request != r) {\n        return luaL_error(L, \"bad request\");\n    }\n\n    ngx_http_lua_socket_check_busy_connecting(r, u, L);\n    ngx_http_lua_socket_check_busy_reading(r, u, L);\n    ngx_http_lua_socket_check_busy_writing(r, u, L);\n\n    b = &u->buffer;\n\n    if (b->start && ngx_buf_size(b)) {\n        ngx_http_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, b->pos,\n                                                              b->last - b->pos);\n\n        lua_pushnil(L);\n        lua_pushliteral(L, \"unread data in buffer\");\n        return 2;\n    }\n\n    if (c->read->error\n        || c->read->timedout\n        || c->write->error\n        || c->write->timedout)\n    {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"invalid connection\");\n        return 2;\n    }\n\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"failed to handle read event\");\n        return 2;\n    }\n\n    if (ngx_terminate || ngx_exiting) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"lua tcp socket set keepalive while process exiting, \"\n                       \"closing connection %p\", c);\n\n        ngx_http_lua_socket_tcp_finalize(r, u);\n        lua_pushinteger(L, 1);\n        return 1;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"lua tcp socket set keepalive: saving connection %p\", c);\n\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(socket_pool_key));\n    lua_rawget(L, LUA_REGISTRYINDEX);\n\n    /* stack: obj timeout? size? pools */\n\n    lua_rawgeti(L, 1, SOCKET_KEY_INDEX);\n    key.data = (u_char *) lua_tolstring(L, -1, &key.len);\n    if (key.data == NULL) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"key not found\");\n        return 2;\n    }\n\n    dd(\"saving connection to key %s\", lua_tostring(L, -1));\n\n    lua_pushvalue(L, -1);\n    lua_rawget(L, -3);\n    spool = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    /* stack: obj timeout? size? pools cache_key */\n\n    if (spool == NULL) {\n        /* create a new socket pool for the current peer key */\n        if (pool_size <= 0) {\n            msg = lua_pushfstring(L, \"bad \\\"pool_size\\\" option value: %d\",\n                                  pool_size);\n            return luaL_argerror(L, n, msg);\n        }\n\n        ngx_http_lua_socket_tcp_create_socket_pool(L, r, key, pool_size, -1,\n                                                   &spool);\n    }\n\n    if (ngx_queue_empty(&spool->free)) {\n\n        q = ngx_queue_last(&spool->cache);\n        ngx_queue_remove(q);\n\n        item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue);\n\n        ngx_http_lua_socket_tcp_close_connection(item->connection);\n\n        /* only decrease the counter for connections which were counted */\n        if (u->socket_pool != NULL) {\n            u->socket_pool->connections--;\n        }\n\n    } else {\n        q = ngx_queue_head(&spool->free);\n        ngx_queue_remove(q);\n\n        item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue);\n\n        /* we should always increase connections after getting connected,\n         * and decrease connections after getting closed.\n         * however, we don't create connection pool in previous connect method.\n         * so we increase connections here for backward compatibility.\n         */\n        if (u->socket_pool == NULL) {\n            spool->connections++;\n        }\n    }\n\n    item->connection = c;\n    ngx_queue_insert_head(&spool->cache, q);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"lua tcp socket clear current socket connection\");\n\n    pc->connection = NULL;\n\n#if 0\n    if (u->cleanup) {\n        *u->cleanup = NULL;\n        u->cleanup = NULL;\n    }\n#endif\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    if (c->write->timer_set) {\n        ngx_del_timer(c->write);\n    }\n\n#if (NGX_DEBUG)\n    if (timeout == 0) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket keepalive timeout: unlimited\");\n    }\n#endif\n\n    if (timeout) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua tcp socket keepalive timeout: %M ms\", timeout);\n\n        ngx_add_timer(c->read, timeout);\n    }\n\n    c->write->handler = ngx_http_lua_socket_keepalive_dummy_handler;\n    c->read->handler = ngx_http_lua_socket_keepalive_rev_handler;\n\n    c->data = item;\n    c->idle = 1;\n    c->log = ngx_cycle->log;\n    c->pool->log = ngx_cycle->log;\n    c->read->log = ngx_cycle->log;\n    c->write->log = ngx_cycle->log;\n\n    item->socklen = pc->socklen;\n    ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);\n    ngx_memcpy(item->host, u->host, sizeof(u->host));\n    item->reused = u->reused;\n    item->udata_queue = u->udata_queue;\n    u->udata_queue = NULL;\n\n    if (c->read->ready) {\n        rc = ngx_http_lua_socket_keepalive_close_handler(c->read);\n        if (rc != NGX_OK) {\n            ngx_http_lua_socket_tcp_finalize(r, u);\n            lua_pushnil(L);\n            lua_pushliteral(L, \"connection in dubious state\");\n            return 2;\n        }\n    }\n\n#if 1\n    ngx_http_lua_socket_tcp_finalize(r, u);\n#endif\n\n    /* since we set u->peer->connection to NULL previously, the connect\n     * operation won't be resumed in the ngx_http_lua_socket_tcp_finalize.\n     * Therefore we need to resume it here.\n     */\n    ngx_http_lua_socket_tcp_resume_conn_op(spool);\n\n    lua_pushinteger(L, 1);\n    return 1;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_get_keepalive_peer(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_http_lua_socket_pool_item_t     *item;\n    ngx_http_lua_socket_pool_t          *spool;\n    ngx_http_cleanup_t                  *cln;\n    ngx_queue_t                         *q;\n    ngx_peer_connection_t               *pc;\n    ngx_connection_t                    *c;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket pool get keepalive peer\");\n\n    pc = &u->peer;\n    spool = u->socket_pool;\n\n    if (!ngx_queue_empty(&spool->cache)) {\n        q = ngx_queue_head(&spool->cache);\n\n        item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue);\n        c = item->connection;\n\n        ngx_queue_remove(q);\n        ngx_queue_insert_head(&spool->free, q);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"lua tcp socket get keepalive peer: using connection %p,\"\n                       \" fd:%d\", c, c->fd);\n\n        c->idle = 0;\n        c->log = pc->log;\n        c->pool->log = pc->log;\n        c->read->log = pc->log;\n        c->write->log = pc->log;\n        c->data = u;\n\n#if 1\n        c->write->handler = ngx_http_lua_socket_tcp_handler;\n        c->read->handler = ngx_http_lua_socket_tcp_handler;\n#endif\n\n        if (c->read->timer_set) {\n            ngx_del_timer(c->read);\n        }\n\n        pc->connection = c;\n        pc->cached = 1;\n        pc->socklen = item->socklen;\n        ngx_memcpy(&u->sockaddr, &item->sockaddr, item->socklen);\n        ngx_memcpy(u->host, item->host, sizeof(item->host));\n\n        u->log.handler = ngx_http_lua_socket_tcp_log_error;\n        u->log.data = u;\n        u->reused = item->reused + 1;\n        u->udata_queue = item->udata_queue;\n        item->udata_queue = NULL;\n\n#if 1\n        u->write_event_handler = ngx_http_lua_socket_dummy_handler;\n        u->read_event_handler = ngx_http_lua_socket_dummy_handler;\n#endif\n\n        if (u->cleanup == NULL) {\n            cln = ngx_http_lua_cleanup_add(r, 0);\n            if (cln == NULL) {\n                u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR;\n                return NGX_ERROR;\n            }\n\n            cln->handler = ngx_http_lua_socket_tcp_cleanup;\n            cln->data = u;\n            u->cleanup = &cln->handler;\n        }\n\n        return NGX_OK;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"lua tcp socket keepalive: connection pool empty\");\n\n    return NGX_DECLINED;\n}\n\n\nstatic void\nngx_http_lua_socket_keepalive_dummy_handler(ngx_event_t *ev)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"keepalive dummy handler\");\n}\n\n\nstatic void\nngx_http_lua_socket_keepalive_rev_handler(ngx_event_t *ev)\n{\n    (void) ngx_http_lua_socket_keepalive_close_handler(ev);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_keepalive_close_handler(ngx_event_t *ev)\n{\n    ngx_http_lua_socket_pool_item_t     *item;\n    ngx_http_lua_socket_pool_t          *spool;\n\n    int                n;\n    unsigned char      buf[1];\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    if (c->close) {\n        goto close;\n    }\n\n    if (c->read->timedout) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                       \"lua tcp socket keepalive max idle timeout\");\n\n        goto close;\n    }\n\n    dd(\"read event ready: %d\", (int) c->read->ready);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"lua tcp socket keepalive close handler check stale events\");\n\n    /* consume the possible ssl-layer data implicitly */\n    n = c->recv(c, buf, 1);\n\n    if (n == NGX_AGAIN) {\n        /* stale event */\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            goto close;\n        }\n\n        return NGX_OK;\n    }\n\nclose:\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"lua tcp socket keepalive close handler: fd:%d\", c->fd);\n\n    item = c->data;\n    spool = item->socket_pool;\n\n    ngx_http_lua_socket_tcp_close_connection(c);\n\n    ngx_queue_remove(&item->queue);\n    ngx_queue_insert_head(&spool->free, &item->queue);\n    spool->connections--;\n\n    dd(\"keepalive: connections: %u\", (unsigned) spool->connections);\n\n    if (spool->connections == 0) {\n        ngx_http_lua_socket_free_pool(ev->log, spool);\n\n    } else {\n        ngx_http_lua_socket_tcp_resume_conn_op(spool);\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic void\nngx_http_lua_socket_free_pool(ngx_log_t *log, ngx_http_lua_socket_pool_t *spool)\n{\n    lua_State                           *L;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"lua tcp socket keepalive: free connection pool for \\\"%s\\\"\",\n                   spool->key);\n\n    L = spool->lua_vm;\n\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(socket_pool_key));\n    lua_rawget(L, LUA_REGISTRYINDEX);\n    lua_pushstring(L, (char *) spool->key);\n    lua_pushnil(L);\n    lua_rawset(L, -3);\n    lua_pop(L, 1);\n}\n\n\nstatic void\nngx_http_lua_socket_shutdown_pool_helper(ngx_http_lua_socket_pool_t *spool)\n{\n    ngx_queue_t                             *q;\n    ngx_connection_t                        *c;\n    ngx_http_lua_socket_pool_item_t         *item;\n    ngx_http_lua_socket_tcp_conn_op_ctx_t   *conn_op_ctx;\n\n    while (!ngx_queue_empty(&spool->cache)) {\n        q = ngx_queue_head(&spool->cache);\n\n        item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue);\n        c = item->connection;\n\n        ngx_http_lua_socket_tcp_close_connection(c);\n\n        ngx_queue_remove(q);\n        ngx_queue_insert_head(&spool->free, q);\n    }\n\n    while (!ngx_queue_empty(&spool->cache_connect_op)) {\n        q = ngx_queue_head(&spool->cache_connect_op);\n        ngx_queue_remove(q);\n        conn_op_ctx = ngx_queue_data(q, ngx_http_lua_socket_tcp_conn_op_ctx_t,\n                                     queue);\n        ngx_http_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx);\n    }\n\n    while (!ngx_queue_empty(&spool->wait_connect_op)) {\n        q = ngx_queue_head(&spool->wait_connect_op);\n        ngx_queue_remove(q);\n        conn_op_ctx = ngx_queue_data(q, ngx_http_lua_socket_tcp_conn_op_ctx_t,\n                                     queue);\n\n        if (conn_op_ctx->event.timer_set) {\n            ngx_del_timer(&conn_op_ctx->event);\n        }\n\n        ngx_http_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx);\n    }\n\n    /* spool->connections will be decreased down to zero in\n     * ngx_http_lua_socket_tcp_finalize */\n}\n\n\nstatic int\nngx_http_lua_socket_shutdown_pool(lua_State *L)\n{\n    ngx_http_lua_socket_pool_t          *spool;\n\n    spool = lua_touserdata(L, 1);\n\n    if (spool != NULL) {\n        ngx_http_lua_socket_shutdown_pool_helper(spool);\n    }\n\n    return 0;\n}\n\n\nstatic int\nngx_http_lua_socket_tcp_upstream_destroy(lua_State *L)\n{\n    ngx_http_lua_socket_tcp_upstream_t      *u;\n\n    dd(\"upstream destroy triggered by Lua GC\");\n\n    u = lua_touserdata(L, 1);\n    if (u == NULL) {\n        return 0;\n    }\n\n    if (u->cleanup) {\n        ngx_http_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */\n    }\n\n    return 0;\n}\n\n\nstatic int\nngx_http_lua_socket_downstream_destroy(lua_State *L)\n{\n    ngx_http_lua_socket_tcp_upstream_t     *u;\n\n    dd(\"downstream destroy\");\n\n    u = lua_touserdata(L, 1);\n    if (u == NULL) {\n        dd(\"u is NULL\");\n        return 0;\n    }\n\n    if (u->cleanup) {\n        ngx_http_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */\n    }\n\n    return 0;\n}\n\n\nstatic void\nngx_http_lua_socket_push_input_data(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, ngx_http_lua_socket_tcp_upstream_t *u,\n    lua_State *L)\n{\n    ngx_chain_t             *cl;\n    ngx_chain_t            **ll;\n#if (DDEBUG) || (NGX_DTRACE)\n    size_t                   size = 0;\n#endif\n    size_t                   chunk_size;\n    ngx_buf_t               *b;\n    size_t                   nbufs;\n    luaL_Buffer              luabuf;\n\n    dd(\"bufs_in: %p, buf_in: %p\", u->bufs_in, u->buf_in);\n\n    nbufs = 0;\n    ll = NULL;\n\n    luaL_buffinit(L, &luabuf);\n\n    for (cl = u->bufs_in; cl; cl = cl->next) {\n        b = cl->buf;\n        chunk_size = b->last - b->pos;\n\n        dd(\"copying input data chunk from %p: \\\"%.*s\\\"\", cl,\n           (int) chunk_size, b->pos);\n\n        luaL_addlstring(&luabuf, (char *) b->pos, chunk_size);\n\n        if (cl->next) {\n            ll = &cl->next;\n        }\n\n#if (DDEBUG) || (NGX_DTRACE)\n        size += chunk_size;\n#endif\n\n        nbufs++;\n    }\n\n    luaL_pushresult(&luabuf);\n\n#if (DDEBUG)\n    dd(\"size: %d, nbufs: %d\", (int) size, (int) nbufs);\n#endif\n\n#if (NGX_DTRACE)\n    ngx_http_lua_probe_socket_tcp_receive_done(r, u,\n                                               (u_char *) lua_tostring(L, -1),\n                                               size);\n#endif\n\n    if (nbufs > 1 && ll) {\n        dd(\"recycle buffers: %d\", (int) (nbufs - 1));\n\n        *ll = ctx->free_recv_bufs;\n        ctx->free_recv_bufs = u->bufs_in;\n        u->bufs_in = u->buf_in;\n    }\n\n    if (u->buffer.pos == u->buffer.last) {\n        dd(\"resetting u->buffer pos & last\");\n        u->buffer.pos = u->buffer.start;\n        u->buffer.last = u->buffer.start;\n    }\n\n    if (u->bufs_in) {\n        u->buf_in->buf->last = u->buffer.pos;\n        u->buf_in->buf->pos = u->buffer.pos;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_add_input_buffer(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    ngx_chain_t             *cl;\n    ngx_http_lua_ctx_t      *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,\n                                         &ctx->free_recv_bufs,\n                                         u->conf->buffer_size);\n\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    u->buf_in->next = cl;\n    u->buf_in = cl;\n    u->buffer = *cl->buf;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_add_pending_data(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, u_char *pos, size_t len, u_char *pat,\n    int prefix, int old_state)\n{\n    u_char          *last;\n    ngx_buf_t       *b;\n\n    dd(\"resuming data: %d: [%.*s]\", prefix, prefix, pat);\n\n    last = &pos[len];\n\n    b = u->buf_in->buf;\n\n    if (last - b->last == old_state) {\n        b->last += prefix;\n        return NGX_OK;\n    }\n\n    dd(\"need more buffers because %d != %d\", (int) (last - b->last),\n       (int) old_state);\n\n    if (ngx_http_lua_socket_insert_buffer(r, u, pat, prefix) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    b->pos = last;\n    b->last = last;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t ngx_http_lua_socket_insert_buffer(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, u_char *pat, size_t prefix)\n{\n    ngx_chain_t             *cl, *new_cl, **ll;\n    ngx_http_lua_ctx_t      *ctx;\n    size_t                   size;\n    ngx_buf_t               *b;\n\n    if (prefix <= u->conf->buffer_size) {\n        size = u->conf->buffer_size;\n\n    } else {\n        size = prefix;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    new_cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,\n                                             &ctx->free_recv_bufs,\n                                             size);\n\n    if (new_cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    b = new_cl->buf;\n\n    b->last = ngx_copy(b->last, pat, prefix);\n\n    dd(\"copy resumed data to %p: %d: \\\"%.*s\\\"\",\n       new_cl, (int) (b->last - b->pos), (int) (b->last - b->pos), b->pos);\n\n    dd(\"before resuming data: bufs_in %p, buf_in %p, buf_in next %p\",\n       u->bufs_in, u->buf_in, u->buf_in->next);\n\n    ll = &u->bufs_in;\n    for (cl = u->bufs_in; cl->next; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    *ll = new_cl;\n    new_cl->next = u->buf_in;\n\n    dd(\"after resuming data: bufs_in %p, buf_in %p, buf_in next %p\",\n       u->bufs_in, u->buf_in, u->buf_in->next);\n\n#if (DDEBUG)\n    for (cl = u->bufs_in; cl; cl = cl->next) {\n        b = cl->buf;\n\n        dd(\"result buf after resuming data: %p: %.*s\", cl,\n           (int) ngx_buf_size(b), b->pos);\n    }\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_tcp_conn_op_resume(ngx_http_request_t *r)\n{\n    return ngx_http_lua_socket_tcp_resume_helper(r, SOCKET_OP_RESUME_CONN);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_tcp_conn_resume(ngx_http_request_t *r)\n{\n    return ngx_http_lua_socket_tcp_resume_helper(r, SOCKET_OP_CONNECT);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_tcp_read_resume(ngx_http_request_t *r)\n{\n    return ngx_http_lua_socket_tcp_resume_helper(r, SOCKET_OP_READ);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_tcp_write_resume(ngx_http_request_t *r)\n{\n    return ngx_http_lua_socket_tcp_resume_helper(r, SOCKET_OP_WRITE);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_tcp_resume_helper(ngx_http_request_t *r, int socket_op)\n{\n    int                                    nret;\n    lua_State                             *vm;\n    ngx_int_t                              rc;\n    ngx_uint_t                             nreqs;\n    ngx_connection_t                      *c;\n    ngx_http_lua_ctx_t                    *ctx;\n    ngx_http_lua_co_ctx_t                 *coctx;\n    ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx;\n\n    ngx_http_lua_socket_tcp_retval_handler  prepare_retvals;\n\n    ngx_http_lua_socket_tcp_upstream_t      *u;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->resume_handler = ngx_http_lua_wev_handler;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp operation done, resuming lua thread\");\n\n    coctx = ctx->cur_co_ctx;\n\n    dd(\"coctx: %p\", coctx);\n\n    switch (socket_op) {\n\n    case SOCKET_OP_RESUME_CONN:\n        conn_op_ctx = coctx->data;\n        u = conn_op_ctx->u;\n        prepare_retvals = u->write_prepare_retvals;\n        break;\n\n    case SOCKET_OP_CONNECT:\n    case SOCKET_OP_WRITE:\n        u = coctx->data;\n        prepare_retvals = u->write_prepare_retvals;\n        break;\n\n    case SOCKET_OP_READ:\n        u = coctx->data;\n        prepare_retvals = u->read_prepare_retvals;\n        break;\n\n    default:\n        /* impossible to reach here */\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua tcp socket calling prepare retvals handler %p, \"\n                   \"u:%p\", prepare_retvals, u);\n\n    nret = prepare_retvals(r, u, ctx->cur_co_ctx->co);\n    if (socket_op == SOCKET_OP_CONNECT\n        && nret > 1\n        && !u->conn_closed\n        && u->socket_pool != NULL)\n    {\n        u->socket_pool->connections--;\n        ngx_http_lua_socket_tcp_resume_conn_op(u->socket_pool);\n    }\n\n    if (nret == NGX_AGAIN) {\n        return NGX_DONE;\n    }\n\n    c = r->connection;\n    vm = ngx_http_lua_get_lua_vm(r, ctx);\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(vm, r, ctx, nret);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run thread returned %d\", rc);\n\n    if (rc == NGX_AGAIN) {\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (ctx->entered_content_phase) {\n        ngx_http_lua_finalize_request(r, rc);\n        return NGX_DONE;\n    }\n\n    return rc;\n}\n\n\nstatic void\nngx_http_lua_tcp_queue_conn_op_cleanup(void *data)\n{\n    ngx_http_lua_co_ctx_t                  *coctx = data;\n    ngx_http_lua_socket_tcp_upstream_t     *u;\n    ngx_http_lua_socket_tcp_conn_op_ctx_t  *conn_op_ctx;\n\n    conn_op_ctx = coctx->data;\n    u = conn_op_ctx->u;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua tcp socket abort queueing, conn_op_ctx: %p, u: %p\",\n                   conn_op_ctx, u);\n\n#if (nginx_version >= 1007005)\n    if (conn_op_ctx->event.posted) {\n#else\n    if (conn_op_ctx->event.prev) {\n#endif\n        /*\n        * We need the extra parentheses around the argument\n        * of ngx_delete_posted_event() just to work around macro issues in\n        * nginx cores older than 1.7.5 (exclusive).\n        */\n        ngx_delete_posted_event((&conn_op_ctx->event));\n\n    } else if (conn_op_ctx->event.timer_set) {\n        ngx_del_timer(&conn_op_ctx->event);\n    }\n\n    ngx_queue_remove(&conn_op_ctx->queue);\n    ngx_queue_insert_head(&u->socket_pool->cache_connect_op,\n                          &conn_op_ctx->queue);\n\n    u->socket_pool->connections--;\n    ngx_http_lua_socket_tcp_resume_conn_op(u->socket_pool);\n}\n\n\nstatic void\nngx_http_lua_tcp_resolve_cleanup(void *data)\n{\n    ngx_resolver_ctx_t                      *rctx;\n    ngx_http_lua_socket_tcp_upstream_t      *u;\n    ngx_http_lua_co_ctx_t                   *coctx = data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua tcp socket abort resolver\");\n\n    u = coctx->data;\n    if (u == NULL) {\n        return;\n    }\n\n    if (u->socket_pool != NULL) {\n        u->socket_pool->connections--;\n        ngx_http_lua_socket_tcp_resume_conn_op(u->socket_pool);\n    }\n\n    rctx = u->resolved->ctx;\n    if (rctx == NULL) {\n        return;\n    }\n\n    /* postpone free the rctx in the handler */\n    rctx->handler = ngx_resolve_name_done;\n}\n\n\nstatic void\nngx_http_lua_coctx_cleanup(void *data)\n{\n    ngx_http_lua_socket_tcp_upstream_t      *u;\n    ngx_http_lua_co_ctx_t                   *coctx = data;\n\n    dd(\"running coctx cleanup\");\n\n    u = coctx->data;\n    if (u == NULL) {\n        return;\n    }\n\n    if (u->request == NULL) {\n        return;\n    }\n\n    ngx_http_lua_socket_tcp_finalize(u->request, u);\n}\n\n\nvoid\nngx_http_lua_cleanup_conn_pools(lua_State *L)\n{\n    ngx_http_lua_socket_pool_t          *spool;\n\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          socket_pool_key));\n    lua_rawget(L, LUA_REGISTRYINDEX); /* table */\n\n    lua_pushnil(L);  /* first key */\n    while (lua_next(L, -2) != 0) {\n        /* tb key val */\n        spool = lua_touserdata(L, -1);\n\n        if (spool != NULL) {\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                           \"lua tcp socket keepalive: free connection pool %p \"\n                           \"for \\\"%s\\\"\", spool, spool->key);\n\n            ngx_http_lua_socket_shutdown_pool_helper(spool);\n        }\n\n        lua_pop(L, 1);\n    }\n\n    lua_pop(L, 1);\n}\n\n\nint\nngx_http_lua_ffi_socket_tcp_init_udata_queue(\n    ngx_http_lua_socket_tcp_upstream_t *u, int capacity, char **err_msg)\n{\n    int                                  i, max_size;\n    ngx_pool_t                          *pool;\n    ngx_http_lua_socket_udata_queue_t   *udata_queue;\n    ngx_http_lua_socket_node_t          *node;\n\n    pool = u->peer.connection->pool;\n\n    if (u->udata_queue == NULL) {\n        max_size = capacity;\n        if (max_size == 0) {\n            max_size = 4;\n        }\n\n        udata_queue = ngx_palloc(pool,\n                                 sizeof(ngx_http_lua_socket_udata_queue_t) +\n                                 sizeof(ngx_http_lua_socket_node_t) * max_size);\n\n        if (udata_queue == NULL) {\n            *err_msg = \"no memory\";\n            return NGX_ERROR;\n        }\n\n        udata_queue->pool = pool;\n        udata_queue->capacity = capacity;\n        udata_queue->len = 0;\n        ngx_queue_init(&udata_queue->queue);\n        ngx_queue_init(&udata_queue->free);\n\n        node = (ngx_http_lua_socket_node_t *) (udata_queue + 1);\n\n        for (i = 0; i < max_size; i++) {\n            ngx_queue_insert_head(&udata_queue->free, &node->queue);\n            node++;\n        }\n\n        u->udata_queue = udata_queue;\n\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0,\n                       \"init udata_queue %uD, cosocket %p udata %p\",\n                       capacity, u, udata_queue);\n    }\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_socket_tcp_count_udata(ngx_http_lua_socket_tcp_upstream_t *u)\n{\n    /* return NGX_ERROR (-1) for missing udata_queue to\n     * distinguish it from empty udata_queue */\n    if (u->udata_queue == NULL) {\n        return NGX_ERROR;\n    }\n\n    return u->udata_queue->len;\n}\n\n\nint\nngx_http_lua_ffi_socket_tcp_add_udata(ngx_http_lua_socket_tcp_upstream_t *u,\n    uint64_t key, uint64_t value, uint64_t *evicted_key,\n    uint64_t *evicted_value, char **err_msg)\n{\n    int                             evicted = 0;\n    ngx_pool_t                     *pool;\n    ngx_http_lua_socket_node_t     *node = NULL;\n    ngx_queue_t                    *q, *uqueue;\n\n    pool = u->peer.connection->pool;\n\n    if (u->udata_queue == NULL) {\n        *err_msg = \"no udata queue\";\n        return NGX_ERROR;\n    }\n\n    uqueue = &u->udata_queue->queue;\n\n    for (q = ngx_queue_head(uqueue);\n         q != ngx_queue_sentinel(uqueue);\n         q = ngx_queue_next(q))\n    {\n        node = ngx_queue_data(q, ngx_http_lua_socket_node_t, queue);\n\n        if (node->key == key) {\n            /* key exists */\n            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0,\n                           \"found %uD, cosocket %p udata %p\",\n                           key, u, u->udata_queue);\n            ngx_queue_remove(q);\n            node->value = value;\n\n            break;\n        }\n    }\n\n    if (q == ngx_queue_sentinel(uqueue)) {\n\n        if (u->udata_queue->capacity\n            && u->udata_queue->capacity == u->udata_queue->len)\n        {\n            /* evict key */\n            q = ngx_queue_last(uqueue);\n            node = ngx_queue_data(q, ngx_http_lua_socket_node_t, queue);\n            ngx_queue_remove(q);\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0,\n                           \"evict %uD for %uD, cosocket %p udata %p\",\n                           node->key, key, u, u->udata_queue);\n            *evicted_key = node->key;\n            *evicted_value = node->value;\n            evicted = 1;\n\n        } else {\n            /* insert key */\n            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0,\n                           \"insert %uD, cosocket %p udata %p\",\n                           key, u, u->udata_queue);\n\n            if (!ngx_queue_empty(&u->udata_queue->free)) {\n                q = ngx_queue_head(&u->udata_queue->free);\n                node = ngx_queue_data(q, ngx_http_lua_socket_node_t, queue);\n                ngx_queue_remove(q);\n                ngx_log_debug3(NGX_LOG_DEBUG_HTTP, u->request->connection->log,\n                               0, \"reuse free node %p, cosocket %p udata %p\",\n                               node, u, u->udata_queue);\n\n            } else {\n                node = ngx_palloc(pool, sizeof(ngx_http_lua_socket_node_t));\n                if (node == NULL) {\n                    goto nomem;\n                }\n\n                ngx_log_debug3(NGX_LOG_DEBUG_HTTP, u->request->connection->log,\n                               0, \"allocate new node %p, cosocket %p udata %p\",\n                               node, u, u->udata_queue);\n            }\n\n            u->udata_queue->len++;\n        }\n\n        node->key = key;\n        node->value = value;\n    }\n\n    ngx_queue_insert_head(uqueue, &node->queue);\n    return evicted ? NGX_DONE : NGX_OK;\n\nnomem:\n\n    *err_msg = \"no memory\";\n    return NGX_ERROR;\n}\n\n\nint\nngx_http_lua_ffi_socket_tcp_get_udata(ngx_http_lua_socket_tcp_upstream_t *u,\n    uint64_t key, uint64_t *value, char **err_msg)\n{\n    ngx_http_lua_socket_node_t     *node;\n    ngx_queue_t                    *q, *uqueue;\n\n    if (u->udata_queue == NULL) {\n        *err_msg = \"no udata queue\";\n        return NGX_ERROR;\n    }\n\n    uqueue = &u->udata_queue->queue;\n\n    for (q = ngx_queue_head(uqueue);\n         q != ngx_queue_sentinel(uqueue);\n         q = ngx_queue_next(q))\n    {\n        node = ngx_queue_data(q, ngx_http_lua_socket_node_t, queue);\n\n        if (node->key == key) {\n            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0,\n                           \"found %uD, cosocket %p udata %p\",\n                           key, u, u->udata_queue);\n            ngx_queue_remove(q);\n            ngx_queue_insert_head(uqueue, &node->queue);\n            *value = node->value;\n            return NGX_OK;\n        }\n    }\n\n    *err_msg = \"not found\";\n    return NGX_ERROR;\n}\n\n\nint\nngx_http_lua_ffi_socket_tcp_del_udata(ngx_http_lua_socket_tcp_upstream_t *u,\n    uint64_t key, char **err_msg)\n{\n    ngx_http_lua_socket_node_t     *node;\n    ngx_queue_t                    *q, *uqueue;\n\n    if (u->udata_queue == NULL) {\n        *err_msg = \"no udata queue\";\n        return NGX_ERROR;\n    }\n\n    uqueue = &u->udata_queue->queue;\n\n    for (q = ngx_queue_head(uqueue);\n         q != ngx_queue_sentinel(uqueue);\n         q = ngx_queue_next(q))\n    {\n        node = ngx_queue_data(q, ngx_http_lua_socket_node_t, queue);\n\n        if (node->key == key) {\n            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0,\n                           \"delete %uD, cosocket %p udata %p\",\n                           key, u, u->udata_queue);\n            ngx_queue_remove(q);\n            ngx_queue_insert_head(&u->udata_queue->free, &node->queue);\n            u->udata_queue->len--;\n            return NGX_OK;\n        }\n    }\n\n    *err_msg = \"not found\";\n    return NGX_ERROR;\n}\n\n\nint\nngx_http_lua_ffi_socket_tcp_getoption(ngx_http_lua_socket_tcp_upstream_t *u,\n    int option, int *val, u_char *err, size_t *errlen)\n{\n    socklen_t len;\n    int       fd, rc;\n\n    if (u == NULL || u->peer.connection == NULL) {\n        *errlen = ngx_snprintf(err, *errlen, \"closed\") - err;\n        return NGX_ERROR;\n    }\n\n    fd = u->peer.connection->fd;\n\n    if (fd == (int) -1) {\n        *errlen = ngx_snprintf(err, *errlen, \"invalid socket fd\") - err;\n        return NGX_ERROR;\n    }\n\n    len = sizeof(int);\n\n    switch (option) {\n    case NGX_HTTP_LUA_SOCKOPT_KEEPALIVE:\n        rc = getsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *) val, &len);\n        break;\n\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n    case NGX_HTTP_LUA_SOCKOPT_KEEPINTVL:\n        rc = getsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, (void *) val, &len);\n        break;\n\n    case NGX_HTTP_LUA_SOCKOPT_KEEPCNT:\n        rc = getsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, (void *) val, &len);\n        break;\n#endif\n\n    case NGX_HTTP_LUA_SOCKOPT_REUSEADDR:\n        rc = getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) val, &len);\n        break;\n\n    case NGX_HTTP_LUA_SOCKOPT_TCP_NODELAY:\n        rc = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *) val, &len);\n        break;\n\n    case NGX_HTTP_LUA_SOCKOPT_SNDBUF:\n        rc = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *) val, &len);\n        break;\n\n    case NGX_HTTP_LUA_SOCKOPT_RCVBUF:\n        rc = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void *) val, &len);\n        break;\n\n    default:\n        *errlen = ngx_snprintf(err, *errlen, \"unsupported option %d\", option)\n                  - err;\n        return NGX_ERROR;\n    }\n\n    if (rc == -1) {\n        *errlen = ngx_strerror(ngx_errno, err, NGX_MAX_ERROR_STR) - err;\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_socket_tcp_setoption(ngx_http_lua_socket_tcp_upstream_t *u,\n    int option, int val, u_char *err, size_t *errlen)\n{\n    socklen_t len;\n    int       fd, rc;\n\n    if (u == NULL || u->peer.connection == NULL) {\n        *errlen = ngx_snprintf(err, *errlen, \"closed\") - err;\n        return NGX_ERROR;\n    }\n\n    fd = u->peer.connection->fd;\n\n    if (fd == (int) -1) {\n        *errlen = ngx_snprintf(err, *errlen, \"invalid socket fd\") - err;\n        return NGX_ERROR;\n    }\n\n    len = sizeof(int);\n\n    switch (option) {\n    case NGX_HTTP_LUA_SOCKOPT_KEEPALIVE:\n        rc = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,\n                        (const void *) &val, len);\n        break;\n\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n    case NGX_HTTP_LUA_SOCKOPT_KEEPINTVL:\n        rc = setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL,\n                        (const void *) &val, len);\n        break;\n\n    case NGX_HTTP_LUA_SOCKOPT_KEEPCNT:\n        rc = setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT,\n                        (const void *) &val, len);\n        break;\n#endif\n\n    case NGX_HTTP_LUA_SOCKOPT_REUSEADDR:\n        rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,\n                        (const void *) &val, len);\n        break;\n\n    case NGX_HTTP_LUA_SOCKOPT_TCP_NODELAY:\n        rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,\n                        (const void *) &val, len);\n        break;\n\n    case NGX_HTTP_LUA_SOCKOPT_SNDBUF:\n        rc = setsockopt(fd, SOL_SOCKET, SO_RCVBUF,\n                        (const void *) &val, len);\n        break;\n\n    case NGX_HTTP_LUA_SOCKOPT_RCVBUF:\n        rc = setsockopt(fd, SOL_SOCKET, SO_SNDBUF,\n                        (const void *) &val, len);\n        break;\n\n    default:\n        *errlen = ngx_snprintf(err, *errlen, \"unsupported option: %d\", option)\n                  - err;\n        return NGX_ERROR;\n    }\n\n    if (rc == -1) {\n        *errlen = ngx_strerror(ngx_errno, err, NGX_MAX_ERROR_STR) - err;\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_socket_tcp_getfd(ngx_http_request_t *r,\n    ngx_http_lua_socket_tcp_upstream_t *u, const char **errmsg)\n{\n    int fd;\n\n    *errmsg = NULL;\n\n    if (u == NULL  || u->peer.connection == NULL) {\n        *errmsg = \"closed\";\n        return -1;\n    }\n\n    fd = u->peer.connection->fd;\n    if (fd == -1) {\n        *errmsg = \"faked connection\";\n    }\n\n    return fd;\n}\n\n\n/* just hack the fd for testing bad case, it will also return the original fd */\nint\nngx_http_lua_ffi_socket_tcp_hack_fd(ngx_http_lua_socket_tcp_upstream_t *u,\n    int fd, u_char *err, size_t *errlen)\n{\n    int rc;\n\n    if (u == NULL || u->peer.connection == NULL) {\n        *errlen = ngx_snprintf(err, *errlen, \"closed\") - err;\n        return -1;\n    }\n\n    rc = u->peer.connection->fd;\n    if (rc == (int) -1) {\n        *errlen = ngx_snprintf(err, *errlen, \"invalid socket fd\") - err;\n        return -1;\n    }\n\n    /* return the original fd value directly when the new fd is invalid */\n    if (fd < 0) {\n        return rc;\n    }\n\n    u->peer.connection->fd = fd;\n\n    return rc;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_socket_tcp.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_SOCKET_TCP_H_INCLUDED_\n#define _NGX_HTTP_LUA_SOCKET_TCP_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\n#define NGX_HTTP_LUA_SOCKET_FT_ERROR         0x0001\n#define NGX_HTTP_LUA_SOCKET_FT_TIMEOUT       0x0002\n#define NGX_HTTP_LUA_SOCKET_FT_CLOSED        0x0004\n#define NGX_HTTP_LUA_SOCKET_FT_RESOLVER      0x0008\n#define NGX_HTTP_LUA_SOCKET_FT_BUFTOOSMALL   0x0010\n#define NGX_HTTP_LUA_SOCKET_FT_NOMEM         0x0020\n#define NGX_HTTP_LUA_SOCKET_FT_PARTIALWRITE  0x0040\n#define NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT   0x0080\n#define NGX_HTTP_LUA_SOCKET_FT_SSL           0x0100\n\n/* max cosocket host length, just for logging,\n * length greater are omitted\n */\n#ifndef COSOCKET_HOST_LEN\n#define COSOCKET_HOST_LEN  32\n#endif\n\n\ntypedef struct ngx_http_lua_socket_tcp_upstream_s\n        ngx_http_lua_socket_tcp_upstream_t;\n\n\ntypedef struct ngx_http_lua_socket_udata_queue_s\n        ngx_http_lua_socket_udata_queue_t;\n\n\ntypedef\n    int (*ngx_http_lua_socket_tcp_retval_handler)(ngx_http_request_t *r,\n        ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L);\n\n\ntypedef void (*ngx_http_lua_socket_tcp_upstream_handler_pt)\n    (ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u);\n\n\ntypedef struct {\n    ngx_event_t                         event;\n    ngx_queue_t                         queue;\n    ngx_str_t                           host;\n    ngx_http_cleanup_pt                *cleanup;\n    ngx_http_lua_socket_tcp_upstream_t *u;\n    in_port_t                           port;\n} ngx_http_lua_socket_tcp_conn_op_ctx_t;\n\n\n#define ngx_http_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx)                \\\n    ngx_free(conn_op_ctx->host.data);                                        \\\n    ngx_free(conn_op_ctx)\n\n\ntypedef struct {\n    lua_State                         *lua_vm;\n\n    ngx_int_t                          size;\n    ngx_queue_t                        cache_connect_op;\n    ngx_queue_t                        wait_connect_op;\n\n    /* connections == active connections + pending connect operations,\n     * while active connections == out-of-pool reused connections\n     *                             + in-pool connections */\n    ngx_int_t                          connections;\n\n    /* queues of ngx_http_lua_socket_pool_item_t: */\n    ngx_queue_t                        cache;\n    ngx_queue_t                        free;\n\n    ngx_int_t                          backlog;\n\n    u_char                             key[1];\n\n} ngx_http_lua_socket_pool_t;\n\n\nstruct ngx_http_lua_socket_tcp_upstream_s {\n    ngx_http_lua_socket_tcp_retval_handler          read_prepare_retvals;\n    ngx_http_lua_socket_tcp_retval_handler          write_prepare_retvals;\n    ngx_http_lua_socket_tcp_upstream_handler_pt     read_event_handler;\n    ngx_http_lua_socket_tcp_upstream_handler_pt     write_event_handler;\n\n    ngx_http_lua_socket_udata_queue_t              *udata_queue;\n\n    ngx_http_lua_socket_pool_t      *socket_pool;\n\n    ngx_http_lua_loc_conf_t         *conf;\n    ngx_http_cleanup_pt             *cleanup;\n    ngx_http_request_t              *request;\n    ngx_peer_connection_t            peer;\n\n    ngx_msec_t                       read_timeout;\n    ngx_msec_t                       send_timeout;\n    ngx_msec_t                       connect_timeout;\n\n    ngx_http_upstream_resolved_t    *resolved;\n\n    ngx_chain_t                     *bufs_in; /* input data buffers */\n    ngx_chain_t                     *buf_in; /* last input data buffer */\n    ngx_buf_t                        buffer; /* receive buffer */\n\n    size_t                           length;\n    size_t                           rest;\n\n    ngx_err_t                        socket_errno;\n\n    ngx_int_t                      (*input_filter)(void *data, ssize_t bytes);\n    void                            *input_filter_ctx;\n\n    size_t                           request_len;\n    ngx_chain_t                     *request_bufs;\n\n    ngx_http_lua_co_ctx_t           *read_co_ctx;\n    ngx_http_lua_co_ctx_t           *write_co_ctx;\n\n    ngx_uint_t                       reused;\n    struct sockaddr_storage          sockaddr;\n    socklen_t                        socklen;\n\n    ngx_log_t                        log;\n    char                             host[COSOCKET_HOST_LEN];\n\n#if (NGX_HTTP_SSL)\n    ngx_str_t                        ssl_name;\n    ngx_ssl_session_t               *ssl_session_ret;\n    const char                      *error_ret;\n    int                              openssl_error_code_ret;\n#endif\n\n    ngx_chain_t                     *busy_bufs;\n\n    unsigned                         ft_type:16;\n    unsigned                         no_close:1;\n    unsigned                         conn_waiting:1;\n    unsigned                         read_waiting:1;\n    unsigned                         write_waiting:1;\n    unsigned                         eof:1;\n    unsigned                         body_downstream:1;\n    unsigned                         raw_downstream:1;\n    unsigned                         read_closed:1;\n    unsigned                         write_closed:1;\n    unsigned                         conn_closed:1;\n#if (NGX_HTTP_SSL)\n    unsigned                         ssl_verify:1;\n    unsigned                         ssl_session_reuse:1;\n#endif\n};\n\n\ntypedef struct ngx_http_lua_dfa_edge_s  ngx_http_lua_dfa_edge_t;\n\n\nstruct ngx_http_lua_dfa_edge_s {\n    u_char                           chr;\n    int                              new_state;\n    ngx_http_lua_dfa_edge_t         *next;\n};\n\n\ntypedef struct {\n    ngx_http_lua_socket_tcp_upstream_t  *upstream;\n\n    ngx_str_t                            pattern;\n    int                                  state;\n    ngx_http_lua_dfa_edge_t            **recovering;\n\n    unsigned                             inclusive:1;\n} ngx_http_lua_socket_compiled_pattern_t;\n\n\ntypedef struct {\n    ngx_http_lua_socket_pool_t      *socket_pool;\n\n    ngx_queue_t                      queue;\n    ngx_connection_t                *connection;\n\n    socklen_t                        socklen;\n    struct sockaddr_storage          sockaddr;\n    char                             host[COSOCKET_HOST_LEN];\n\n    ngx_uint_t                       reused;\n\n    ngx_http_lua_socket_udata_queue_t   *udata_queue;\n} ngx_http_lua_socket_pool_item_t;\n\n\nstruct ngx_http_lua_socket_udata_queue_s {\n    ngx_pool_t                      *pool;\n    ngx_queue_t                      queue;\n    ngx_queue_t                      free;\n    int                              len;\n    int                              capacity;\n};\n\n\ntypedef struct {\n    ngx_queue_t                  queue;\n    uint64_t                     key;\n    uint64_t                     value;\n} ngx_http_lua_socket_node_t;\n\n\nvoid ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L);\nvoid ngx_http_lua_inject_req_socket_api(lua_State *L);\nvoid ngx_http_lua_cleanup_conn_pools(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_SOCKET_TCP_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_socket_udp.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_socket_udp.h\"\n#include \"ngx_http_lua_socket_tcp.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_output.h\"\n#include \"ngx_http_lua_probe.h\"\n\n\n#if 1\n#undef ngx_http_lua_probe_info\n#define ngx_http_lua_probe_info(msg)\n#endif\n\n\n#define UDP_MAX_DATAGRAM_SIZE 65536\n\n\nstatic int ngx_http_lua_socket_udp(lua_State *L);\nstatic int ngx_http_lua_socket_udp_setpeername(lua_State *L);\nstatic int ngx_http_lua_socket_udp_send(lua_State *L);\nstatic int ngx_http_lua_socket_udp_receive(lua_State *L);\nstatic int ngx_http_lua_socket_udp_settimeout(lua_State *L);\nstatic void ngx_http_lua_socket_udp_finalize(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u);\nstatic int ngx_http_lua_socket_udp_upstream_destroy(lua_State *L);\nstatic int ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u, lua_State *L);\nstatic void ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx);\nstatic int ngx_http_lua_socket_error_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u, lua_State *L);\nstatic void ngx_http_lua_socket_udp_handle_error(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u, ngx_uint_t ft_type);\nstatic void ngx_http_lua_socket_udp_cleanup(void *data);\nstatic void ngx_http_lua_socket_udp_handler(ngx_event_t *ev);\nstatic void ngx_http_lua_socket_dummy_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u);\nstatic int ngx_http_lua_socket_udp_receive_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u, lua_State *L);\nstatic ngx_int_t ngx_http_lua_socket_udp_read(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u);\nstatic void ngx_http_lua_socket_udp_read_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u);\nstatic void ngx_http_lua_socket_udp_handle_success(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u);\nstatic ngx_int_t ngx_http_lua_udp_connect(ngx_http_lua_udp_connection_t *uc,\n    ngx_addr_t *local);\nstatic int ngx_http_lua_socket_udp_close(lua_State *L);\nstatic ngx_int_t ngx_http_lua_socket_udp_resume(ngx_http_request_t *r);\nstatic void ngx_http_lua_udp_resolve_cleanup(void *data);\nstatic void ngx_http_lua_udp_socket_cleanup(void *data);\nstatic int ngx_http_lua_socket_udp_bind(lua_State *L);\n\n\nenum {\n    SOCKET_CTX_INDEX = 1,\n    SOCKET_TIMEOUT_INDEX = 2,\n    SOCKET_BIND_INDEX = 3,\n};\n\n\nstatic char ngx_http_lua_socket_udp_metatable_key;\nstatic char ngx_http_lua_udp_udata_metatable_key;\nstatic u_char ngx_http_lua_socket_udp_buffer[UDP_MAX_DATAGRAM_SIZE];\n\n\nvoid\nngx_http_lua_inject_socket_udp_api(ngx_log_t *log, lua_State *L)\n{\n    lua_getfield(L, -1, \"socket\"); /* ngx socket */\n\n    lua_pushcfunction(L, ngx_http_lua_socket_udp);\n    lua_setfield(L, -2, \"udp\"); /* ngx socket */\n\n    /* udp socket object metatable */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          socket_udp_metatable_key));\n    lua_createtable(L, 0 /* narr */, 6 /* nrec */);\n\n    lua_pushcfunction(L, ngx_http_lua_socket_udp_setpeername);\n    lua_setfield(L, -2, \"setpeername\"); /* ngx socket mt */\n\n    lua_pushcfunction(L, ngx_http_lua_socket_udp_send);\n    lua_setfield(L, -2, \"send\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_udp_receive);\n    lua_setfield(L, -2, \"receive\");\n\n    lua_pushcfunction(L, ngx_http_lua_socket_udp_settimeout);\n    lua_setfield(L, -2, \"settimeout\"); /* ngx socket mt */\n\n    lua_pushcfunction(L, ngx_http_lua_socket_udp_close);\n    lua_setfield(L, -2, \"close\"); /* ngx socket mt */\n\n    lua_pushcfunction(L, ngx_http_lua_socket_udp_bind);\n    lua_setfield(L, -2, \"bind\");\n\n    lua_pushvalue(L, -1);\n    lua_setfield(L, -2, \"__index\");\n    lua_rawset(L, LUA_REGISTRYINDEX);\n    /* }}} */\n\n    /* udp socket object metatable */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          udp_udata_metatable_key));\n    lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */\n    lua_pushcfunction(L, ngx_http_lua_socket_udp_upstream_destroy);\n    lua_setfield(L, -2, \"__gc\");\n    lua_rawset(L, LUA_REGISTRYINDEX);\n    /* }}} */\n\n    lua_pop(L, 1);\n}\n\n\nstatic u_char *\nngx_http_lua_socket_udp_log_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char                 *p;\n    ngx_http_request_t     *r;\n\n    ngx_http_lua_udp_connection_t       *uc;\n    ngx_http_lua_socket_udp_upstream_t  *u;\n\n    u = log->data;\n    if (u->port == 0) {\n        p = ngx_snprintf(buf, len, \", upstream: %V\", &u->host);\n\n    } else {\n        p = ngx_snprintf(buf, len, \", upstream: %V:%d\", &u->host, u->port);\n    }\n\n    len -= p - buf;\n    uc = &u->udp_connection;\n    if (uc->sockaddr != NULL) {\n        int    addr_text_len;\n        u_char addr_text[NGX_UNIX_ADDRSTRLEN];\n\n        buf = p;\n        addr_text_len = ngx_sock_ntop(uc->sockaddr, uc->socklen,\n                                      addr_text, NGX_UNIX_ADDRSTRLEN, 0);\n        p = ngx_snprintf(buf, len, \"(%*s)\", addr_text_len, addr_text);\n\n        len -= p - buf;\n    }\n\n    r = u->request;\n    if (r != NULL) {\n        return r->connection->log->handler(r->connection->log, p, len);\n    }\n\n    return p;\n}\n\n\nstatic int\nngx_http_lua_socket_udp(lua_State *L)\n{\n    ngx_http_request_t      *r;\n    ngx_http_lua_ctx_t      *ctx;\n\n    if (lua_gettop(L) != 0) {\n        return luaL_error(L, \"expecting zero arguments, but got %d\",\n                          lua_gettop(L));\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    lua_createtable(L, 3 /* narr */, 1 /* nrec */);\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          socket_udp_metatable_key));\n    lua_rawget(L, LUA_REGISTRYINDEX);\n    lua_setmetatable(L, -2);\n\n    dd(\"top: %d\", lua_gettop(L));\n\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_socket_udp_setpeername(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_str_t                    host;\n    ngx_addr_t                  *local;\n    int                          port;\n    ngx_resolver_ctx_t          *rctx, temp;\n    ngx_http_core_loc_conf_t    *clcf;\n    int                          saved_top;\n    int                          n;\n    u_char                      *p;\n    size_t                       len;\n    ngx_url_t                    url;\n    ngx_int_t                    rc;\n    ngx_http_lua_loc_conf_t     *llcf;\n    int                          timeout;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    ngx_http_lua_udp_connection_t           *uc;\n    ngx_http_lua_socket_udp_upstream_t      *u;\n\n    /*\n     * TODO: we should probably accept an extra argument to setpeername()\n     * to allow the user bind the datagram unix domain socket himself,\n     * which is necessary for systems without autobind support.\n     */\n\n    n = lua_gettop(L);\n    if (n != 2 && n != 3) {\n        return luaL_error(L, \"ngx.socket.udp setpeername: expecting 2 or 3 \"\n                          \"arguments (including the object), but seen %d\", n);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    p = (u_char *) luaL_checklstring(L, 2, &len);\n\n    host.data = ngx_palloc(r->pool, len + 1);\n    if (host.data == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    host.len = len;\n\n    ngx_memcpy(host.data, p, len);\n    host.data[len] = '\\0';\n\n    if (n == 3) {\n        port = luaL_checkinteger(L, 3);\n\n        if (port < 0 || port > 65535) {\n            lua_pushnil(L);\n            lua_pushfstring(L, \"bad port number: %d\", port);\n            return 2;\n        }\n\n    } else { /* n == 2 */\n        port = 0;\n    }\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    if (u) {\n        if (u->request && u->request != r) {\n            return luaL_error(L, \"bad request\");\n        }\n\n        if (u->waiting) {\n            lua_pushnil(L);\n            lua_pushliteral(L, \"socket busy\");\n            return 2;\n        }\n\n        if (u->udp_connection.connection) {\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"lua udp socket reconnect without shutting down\");\n\n            ngx_http_lua_socket_udp_finalize(r, u);\n        }\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua reuse socket upstream ctx\");\n\n    } else {\n        u = lua_newuserdata(L, sizeof(ngx_http_lua_socket_udp_upstream_t));\n        if (u == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n#if 1\n        lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                              udp_udata_metatable_key));\n        lua_rawget(L, LUA_REGISTRYINDEX);\n        lua_setmetatable(L, -2);\n#endif\n\n        lua_rawseti(L, 1, SOCKET_CTX_INDEX);\n    }\n\n    ngx_memzero(u, sizeof(ngx_http_lua_socket_udp_upstream_t));\n\n    u->request = r; /* set the controlling request */\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    u->conf = llcf;\n\n    uc = &u->udp_connection;\n\n    uc->log = *r->connection->log;\n    uc->log.data = u;\n    uc->log.handler = ngx_http_lua_socket_udp_log_error;\n\n    u->host = host;\n    u->port = port;\n\n    dd(\"lua peer connection log: %p\", &uc->log);\n\n    lua_rawgeti(L, 1, SOCKET_TIMEOUT_INDEX);\n    timeout = (ngx_int_t) lua_tointeger(L, -1);\n    lua_pop(L, 1);\n\n    if (timeout > 0) {\n        u->read_timeout = (ngx_msec_t) timeout;\n\n    } else {\n        u->read_timeout = u->conf->read_timeout;\n    }\n\n    lua_rawgeti(L, 1, SOCKET_BIND_INDEX);\n    local = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n    if (local != NULL) {\n        u->local = local;\n    }\n\n    ngx_memzero(&url, sizeof(ngx_url_t));\n\n    url.url.len = host.len;\n    url.url.data = host.data;\n    url.default_port = (in_port_t) port;\n    url.no_resolve = 1;\n\n    if (ngx_parse_url(r->pool, &url) != NGX_OK) {\n        lua_pushnil(L);\n\n        if (url.err) {\n            lua_pushfstring(L, \"failed to parse host name \\\"%s\\\": %s\",\n                            host.data, url.err);\n\n        } else {\n            lua_pushfstring(L, \"failed to parse host name \\\"%s\\\"\", host.data);\n        }\n\n        return 2;\n    }\n\n    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));\n    if (u->resolved == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    if (url.addrs && url.addrs[0].sockaddr) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua udp socket network address given directly\");\n\n        u->resolved->sockaddr = url.addrs[0].sockaddr;\n        u->resolved->socklen = url.addrs[0].socklen;\n        u->resolved->naddrs = 1;\n        u->resolved->host = url.addrs[0].name;\n\n    } else {\n        u->resolved->host = host;\n        u->resolved->port = (in_port_t) port;\n    }\n\n    if (u->resolved->sockaddr) {\n        rc = ngx_http_lua_socket_resolve_retval_handler(r, u, L);\n        if (rc == NGX_AGAIN) {\n            return lua_yield(L, 0);\n        }\n\n        return rc;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    temp.name = host;\n    rctx = ngx_resolve_start(clcf->resolver, &temp);\n    if (rctx == NULL) {\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER;\n        lua_pushnil(L);\n        lua_pushliteral(L, \"failed to start the resolver\");\n        return 2;\n    }\n\n    if (rctx == NGX_NO_RESOLVER) {\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER;\n        lua_pushnil(L);\n        lua_pushfstring(L, \"no resolver defined to resolve \\\"%s\\\"\", host.data);\n        return 2;\n    }\n\n    rctx->name = host;\n    rctx->handler = ngx_http_lua_socket_resolve_handler;\n    rctx->data = u;\n    rctx->timeout = clcf->resolver_timeout;\n\n    u->co_ctx = ctx->cur_co_ctx;\n    u->resolved->ctx = rctx;\n\n    saved_top = lua_gettop(L);\n\n    coctx = ctx->cur_co_ctx;\n    ngx_http_lua_cleanup_pending_operation(coctx);\n    coctx->cleanup = ngx_http_lua_udp_resolve_cleanup;\n\n    if (ngx_resolve_name(rctx) != NGX_OK) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua udp socket fail to run resolver immediately\");\n\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER;\n\n        u->resolved->ctx = NULL;\n        lua_pushnil(L);\n        lua_pushfstring(L, \"%s could not be resolved\", host.data);\n\n        return 2;\n    }\n\n    if (u->waiting == 1) {\n        /* resolved and already connecting */\n        return lua_yield(L, 0);\n    }\n\n    n = lua_gettop(L) - saved_top;\n    if (n) {\n        /* errors occurred during resolving or connecting\n         * or already connected */\n        return n;\n    }\n\n    /* still resolving */\n\n    u->waiting = 1;\n    u->prepare_retvals = ngx_http_lua_socket_resolve_retval_handler;\n\n    coctx->data = u;\n\n    if (ctx->entered_content_phase) {\n        r->write_event_handler = ngx_http_lua_content_wev_handler;\n\n    } else {\n        r->write_event_handler = ngx_http_core_run_phases;\n    }\n\n    return lua_yield(L, 0);\n}\n\n\nstatic void\nngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx)\n{\n    ngx_http_request_t                  *r;\n    ngx_connection_t                    *c;\n    ngx_http_upstream_resolved_t        *ur;\n    ngx_http_lua_ctx_t                  *lctx;\n    lua_State                           *L;\n    ngx_http_lua_socket_udp_upstream_t  *u;\n    u_char                              *p;\n    size_t                               len;\n    socklen_t                            socklen;\n    struct sockaddr                     *sockaddr;\n    ngx_uint_t                           i;\n    unsigned                             waiting;\n\n    u = ctx->data;\n    r = u->request;\n    c = r->connection;\n    ur = u->resolved;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"lua udp socket resolve handler\");\n\n    lctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (lctx == NULL) {\n        return;\n    }\n\n    lctx->cur_co_ctx = u->co_ctx;\n\n    u->co_ctx->cleanup = NULL;\n\n    L = lctx->cur_co_ctx->co;\n\n    dd(\"setting socket_ready to 1\");\n\n    waiting = u->waiting;\n\n    if (ctx->state) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"lua udp socket resolver error: %s (waiting: %d)\",\n                       ngx_resolver_strerror(ctx->state), (int) u->waiting);\n\n        lua_pushnil(L);\n        lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len);\n        lua_pushfstring(L, \" could not be resolved (%d: %s)\",\n                        (int) ctx->state,\n                        ngx_resolver_strerror(ctx->state));\n        lua_concat(L, 2);\n\n#if 1\n        ngx_resolve_name_done(ctx);\n        ur->ctx = NULL;\n#endif\n\n        u->prepare_retvals = ngx_http_lua_socket_error_retval_handler;\n        ngx_http_lua_socket_udp_handle_error(r, u,\n                                             NGX_HTTP_LUA_SOCKET_FT_RESOLVER);\n\n        if (waiting) {\n            ngx_http_run_posted_requests(c);\n        }\n\n        return;\n    }\n\n    ur->naddrs = ctx->naddrs;\n    ur->addrs = ctx->addrs;\n\n#if (NGX_DEBUG)\n    {\n        u_char      text[NGX_SOCKADDR_STRLEN];\n        ngx_str_t   addr;\n        ngx_uint_t  i;\n\n        addr.data = text;\n\n        for (i = 0; i < ctx->naddrs; i++) {\n            addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr,\n                                     ur->addrs[i].socklen, text,\n                                     NGX_SOCKADDR_STRLEN, 0);\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"name was resolved to %V\", &addr);\n        }\n    }\n#endif\n\n    ngx_http_lua_assert(ur->naddrs > 0);\n\n    if (ur->naddrs == 1) {\n        i = 0;\n\n    } else {\n        i = ngx_random() % ur->naddrs;\n    }\n\n    dd(\"selected addr index: %d\", (int) i);\n\n    socklen = ur->addrs[i].socklen;\n\n    sockaddr = ngx_palloc(r->pool, socklen);\n    if (sockaddr == NULL) {\n        goto nomem;\n    }\n\n    ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen);\n\n    switch (sockaddr->sa_family) {\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port);\n        break;\n#endif\n    default: /* AF_INET */\n        ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port);\n    }\n\n    p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN);\n    if (p == NULL) {\n        goto nomem;\n    }\n\n    len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);\n    ur->sockaddr = sockaddr;\n    ur->socklen = socklen;\n\n    ur->host.data = p;\n    ur->host.len = len;\n    ur->naddrs = 1;\n\n    ngx_resolve_name_done(ctx);\n    ur->ctx = NULL;\n\n    u->waiting = 0;\n\n    if (waiting) {\n        lctx->resume_handler = ngx_http_lua_socket_udp_resume;\n        r->write_event_handler(r);\n        ngx_http_run_posted_requests(c);\n\n    } else {\n        (void) ngx_http_lua_socket_resolve_retval_handler(r, u, L);\n    }\n\n    return;\n\nnomem:\n\n    if (ur->ctx) {\n        ngx_resolve_name_done(ctx);\n        ur->ctx = NULL;\n    }\n\n    u->prepare_retvals = ngx_http_lua_socket_error_retval_handler;\n    ngx_http_lua_socket_udp_handle_error(r, u,\n                                         NGX_HTTP_LUA_SOCKET_FT_NOMEM);\n\n    if (waiting) {\n        ngx_http_run_posted_requests(c);\n\n    } else {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"no memory\");\n    }\n}\n\n\nstatic int\nngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u, lua_State *L)\n{\n    ngx_http_lua_ctx_t              *ctx;\n    ngx_http_lua_co_ctx_t           *coctx;\n    ngx_connection_t                *c;\n    ngx_pool_cleanup_t              *cln;\n    ngx_http_upstream_resolved_t    *ur;\n    ngx_int_t                        rc;\n    ngx_http_lua_udp_connection_t   *uc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua udp socket resolve retval handler\");\n\n    if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_RESOLVER) {\n        return 2;\n    }\n\n    uc = &u->udp_connection;\n\n    ur = u->resolved;\n\n    if (ur->sockaddr) {\n        uc->sockaddr = ur->sockaddr;\n        uc->socklen = ur->socklen;\n        uc->server = ur->host;\n\n    } else {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"resolver not working\");\n        return 2;\n    }\n\n    rc = ngx_http_lua_udp_connect(uc, u->local);\n\n    if (rc != NGX_OK) {\n        u->socket_errno = ngx_socket_errno;\n    }\n\n    if (u->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n        if (cln == NULL) {\n            u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR;\n            lua_pushnil(L);\n            lua_pushliteral(L, \"no memory\");\n            return 2;\n        }\n\n        cln->handler = ngx_http_lua_socket_udp_cleanup;\n        cln->data = u;\n        u->cleanup = &cln->handler;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua udp socket connect: %i\", rc);\n\n    if (rc != NGX_OK) {\n        return ngx_http_lua_socket_error_retval_handler(r, u, L);\n    }\n\n    /* rc == NGX_OK */\n\n    c = uc->connection;\n\n    c->data = u;\n\n    c->write->handler = NULL;\n    c->read->handler = ngx_http_lua_socket_udp_handler;\n    c->read->resolver = 0;\n\n    c->pool = r->pool;\n    c->log = &uc->log;\n    c->read->log = c->log;\n    c->write->log = c->log;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    coctx = ctx->cur_co_ctx;\n\n    coctx->data = u;\n\n    u->read_event_handler = ngx_http_lua_socket_dummy_handler;\n\n    lua_pushinteger(L, 1);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_socket_error_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u, lua_State *L)\n{\n    u_char           errstr[NGX_MAX_ERROR_STR];\n    u_char          *p;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua udp socket error retval handler\");\n\n    if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_RESOLVER) {\n        return 2;\n    }\n\n    lua_pushnil(L);\n\n    if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_PARTIALWRITE) {\n        lua_pushliteral(L, \"partial write\");\n\n    } else if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_TIMEOUT) {\n        lua_pushliteral(L, \"timeout\");\n\n    } else if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_CLOSED) {\n        lua_pushliteral(L, \"closed\");\n\n    } else if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_BUFTOOSMALL) {\n        lua_pushliteral(L, \"buffer too small\");\n\n    } else if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_NOMEM) {\n        lua_pushliteral(L, \"no memory\");\n\n    } else {\n\n        if (u->socket_errno) {\n            p = ngx_strerror(u->socket_errno, errstr, sizeof(errstr));\n            /* for compatibility with LuaSocket */\n            ngx_strlow(errstr, errstr, p - errstr);\n            lua_pushlstring(L, (char *) errstr, p - errstr);\n\n        } else {\n            lua_pushliteral(L, \"error\");\n        }\n    }\n\n    return 2;\n}\n\n\nstatic int\nngx_http_lua_socket_udp_bind(lua_State *L)\n{\n    ngx_http_request_t   *r;\n    ngx_http_lua_ctx_t   *ctx;\n    int                   n;\n    u_char               *text;\n    size_t                len;\n    ngx_addr_t           *local;\n\n    n = lua_gettop(L);\n\n    if (n != 2) {\n        return luaL_error(L, \"expecting 2 arguments, but got %d\",\n                          lua_gettop(L));\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    text = (u_char *) luaL_checklstring(L, 2, &len);\n\n    local = ngx_http_lua_parse_addr(L, text, len);\n    if (local == NULL) {\n        lua_pushnil(L);\n        lua_pushfstring(L, \"bad address\");\n        return 2;\n    }\n\n    lua_rawseti(L, 1, SOCKET_BIND_INDEX);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua udp socket bind ip: %V\", &local->name);\n\n    lua_pushinteger(L, 1);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_socket_udp_send(lua_State *L)\n{\n    ssize_t                              n;\n    ngx_http_request_t                  *r;\n    u_char                              *p;\n    u_char                              *str;\n    size_t                               len;\n    ngx_http_lua_socket_udp_upstream_t  *u;\n    int                                  type;\n    const char                          *msg;\n    ngx_str_t                            query;\n    ngx_http_lua_loc_conf_t             *llcf;\n\n    if (lua_gettop(L) != 2) {\n        return luaL_error(L, \"expecting 2 arguments (including the object), \"\n                          \"but got %d\", lua_gettop(L));\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"request object not found\");\n    }\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    if (u == NULL || u->udp_connection.connection == NULL) {\n        llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n        if (llcf->log_socket_errors) {\n            ngx_log_t  *log;\n\n            log = r->connection->log;\n            if (u != NULL && u->udp_connection.connection != NULL) {\n                log = &u->udp_connection.log;\n            }\n\n            ngx_log_error(NGX_LOG_ERR, log, 0,\n                          \"attempt to send data on a closed socket: u:%p, c:%p\",\n                          u, u ? u->udp_connection.connection : NULL);\n        }\n\n        lua_pushnil(L);\n        lua_pushliteral(L, \"closed\");\n        return 2;\n    }\n\n    if (u->request != r) {\n        return luaL_error(L, \"bad request\");\n    }\n\n    if (u->ft_type) {\n        u->ft_type = 0;\n    }\n\n    if (u->waiting) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"socket busy\");\n        return 2;\n    }\n\n    type = lua_type(L, 2);\n    switch (type) {\n        case LUA_TNUMBER:\n            len = ngx_http_lua_get_num_len(L, 2);\n            break;\n\n        case LUA_TSTRING:\n            lua_tolstring(L, 2, &len);\n            break;\n\n        case LUA_TTABLE:\n            /* The maximum possible length, not the actual length */\n            len = ngx_http_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */);\n            break;\n\n        case LUA_TNIL:\n            len = sizeof(\"nil\") - 1;\n            break;\n\n        case LUA_TBOOLEAN:\n            if (lua_toboolean(L, 2)) {\n                len = sizeof(\"true\") - 1;\n\n            } else {\n                len = sizeof(\"false\") - 1;\n            }\n\n            break;\n\n        default:\n            msg = lua_pushfstring(L, \"string, number, boolean, nil, \"\n                                  \"or array table expected, got %s\",\n                                  lua_typename(L, type));\n\n            return luaL_argerror(L, 2, msg);\n    }\n\n    query.data = lua_newuserdata(L, len);\n    p = query.data;\n\n    switch (type) {\n        case LUA_TNUMBER:\n            p = ngx_http_lua_write_num(L, 2, p);\n            break;\n\n        case LUA_TSTRING:\n            str = (u_char *) lua_tolstring(L, 2, &len);\n            p = ngx_cpymem(p, (u_char *) str, len);\n            break;\n\n        case LUA_TTABLE:\n            p = ngx_http_lua_copy_str_in_table(L, 2, p);\n            break;\n\n        case LUA_TNIL:\n            *p++ = 'n';\n            *p++ = 'i';\n            *p++ = 'l';\n            break;\n\n        case LUA_TBOOLEAN:\n            if (lua_toboolean(L, 2)) {\n                *p++ = 't';\n                *p++ = 'r';\n                *p++ = 'u';\n                *p++ = 'e';\n\n            } else {\n                *p++ = 'f';\n                *p++ = 'a';\n                *p++ = 'l';\n                *p++ = 's';\n                *p++ = 'e';\n            }\n\n            break;\n\n        default:\n            return luaL_error(L, \"impossible to reach here\");\n    }\n\n    query.len = p - query.data;\n    ngx_http_lua_assert(query.len <= len);\n\n    u->ft_type = 0;\n\n    /* mimic ngx_http_upstream_init_request here */\n\n#if 1\n    u->waiting = 0;\n#endif\n\n    dd(\"sending query %.*s\", (int) query.len, query.data);\n\n    n = ngx_send(u->udp_connection.connection, query.data, query.len);\n\n    dd(\"ngx_send returns %d (query len %d)\", (int) n, (int) query.len);\n\n    if (n == NGX_ERROR || n == NGX_AGAIN) {\n        u->socket_errno = ngx_socket_errno;\n\n        return ngx_http_lua_socket_error_retval_handler(r, u, L);\n    }\n\n    if (n != (ssize_t) query.len) {\n        dd(\"not the while query was sent\");\n\n        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_PARTIALWRITE;\n        return ngx_http_lua_socket_error_retval_handler(r, u, L);\n    }\n\n    dd(\"n == len\");\n\n    lua_pushinteger(L, 1);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_socket_udp_receive(lua_State *L)\n{\n    ngx_http_request_t                  *r;\n    ngx_http_lua_socket_udp_upstream_t  *u;\n    ngx_int_t                            rc;\n    ngx_http_lua_ctx_t                  *ctx;\n    ngx_http_lua_co_ctx_t               *coctx;\n    size_t                               size;\n    int                                  nargs;\n    ngx_http_lua_loc_conf_t             *llcf;\n\n    nargs = lua_gettop(L);\n    if (nargs != 1 && nargs != 2) {\n        return luaL_error(L, \"expecting 1 or 2 arguments \"\n                          \"(including the object), but got %d\", nargs);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua udp socket calling receive() method\");\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    if (u == NULL || u->udp_connection.connection == NULL) {\n        llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n        if (llcf->log_socket_errors) {\n            ngx_log_t  *log;\n\n            log = r->connection->log;\n            if (u != NULL && u->udp_connection.connection != NULL) {\n                log = &u->udp_connection.log;\n            }\n\n            ngx_log_error(NGX_LOG_ERR, log, 0,\n                          \"attempt to receive data on a closed socket: u:%p, \"\n                          \"c:%p\", u, u ? u->udp_connection.connection : NULL);\n        }\n\n        lua_pushnil(L);\n        lua_pushliteral(L, \"closed\");\n        return 2;\n    }\n\n    if (u->request != r) {\n        return luaL_error(L, \"bad request\");\n    }\n\n    if (u->ft_type) {\n        u->ft_type = 0;\n    }\n\n#if 1\n    if (u->waiting) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"socket busy\");\n        return 2;\n    }\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua udp socket read timeout: %M\", u->read_timeout);\n\n    size = (size_t) luaL_optnumber(L, 2, UDP_MAX_DATAGRAM_SIZE);\n    size = ngx_min(size, UDP_MAX_DATAGRAM_SIZE);\n\n    u->recv_buf_size = size;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua udp socket receive buffer size: %uz\", u->recv_buf_size);\n\n    rc = ngx_http_lua_socket_udp_read(r, u);\n\n    if (rc == NGX_ERROR) {\n        dd(\"read failed: %d\", (int) u->ft_type);\n        rc = ngx_http_lua_socket_udp_receive_retval_handler(r, u, L);\n        dd(\"udp receive retval returned: %d\", (int) rc);\n        return rc;\n    }\n\n    if (rc == NGX_OK) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua udp socket receive done in a single run\");\n\n        return ngx_http_lua_socket_udp_receive_retval_handler(r, u, L);\n    }\n\n    /* n == NGX_AGAIN */\n\n    u->read_event_handler = ngx_http_lua_socket_udp_read_handler;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    coctx = ctx->cur_co_ctx;\n\n    ngx_http_lua_cleanup_pending_operation(coctx);\n    coctx->cleanup = ngx_http_lua_udp_socket_cleanup;\n    coctx->data = u;\n\n    if (ctx->entered_content_phase) {\n        r->write_event_handler = ngx_http_lua_content_wev_handler;\n\n    } else {\n        r->write_event_handler = ngx_http_core_run_phases;\n    }\n\n    u->co_ctx = coctx;\n    u->waiting = 1;\n    u->prepare_retvals = ngx_http_lua_socket_udp_receive_retval_handler;\n\n    return lua_yield(L, 0);\n}\n\n\nstatic int\nngx_http_lua_socket_udp_receive_retval_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u, lua_State *L)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua udp socket receive return value handler\");\n\n    if (u->ft_type) {\n        return ngx_http_lua_socket_error_retval_handler(r, u, L);\n    }\n\n    lua_pushlstring(L, (char *) ngx_http_lua_socket_udp_buffer, u->received);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_socket_udp_settimeout(lua_State *L)\n{\n    int                     n;\n    ngx_int_t               timeout;\n\n    ngx_http_lua_socket_udp_upstream_t  *u;\n\n    n = lua_gettop(L);\n\n    if (n != 2) {\n        return luaL_error(L, \"ngx.socket settimeout: expecting at least 2 \"\n                          \"arguments (including the object) but seen %d\",\n                          lua_gettop(L));\n    }\n\n    timeout = (ngx_int_t) lua_tonumber(L, 2);\n\n    lua_rawseti(L, 1, SOCKET_TIMEOUT_INDEX);\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n\n    if (u) {\n        if (timeout > 0) {\n            u->read_timeout = (ngx_msec_t) timeout;\n\n        } else {\n            u->read_timeout = u->conf->read_timeout;\n        }\n    }\n\n    return 0;\n}\n\n\nstatic void\nngx_http_lua_socket_udp_finalize(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua finalize socket\");\n\n    if (u->cleanup) {\n        *u->cleanup = NULL;\n        u->cleanup = NULL;\n    }\n\n    if (u->resolved && u->resolved->ctx) {\n        ngx_resolve_name_done(u->resolved->ctx);\n        u->resolved->ctx = NULL;\n    }\n\n    if (u->udp_connection.connection) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua close socket connection\");\n\n        ngx_close_connection(u->udp_connection.connection);\n        u->udp_connection.connection = NULL;\n    }\n\n    if (u->waiting) {\n        u->waiting = 0;\n    }\n}\n\n\nstatic int\nngx_http_lua_socket_udp_upstream_destroy(lua_State *L)\n{\n    ngx_http_lua_socket_udp_upstream_t      *u;\n\n    dd(\"upstream destroy triggered by Lua GC\");\n\n    u = lua_touserdata(L, 1);\n    if (u == NULL) {\n        return 0;\n    }\n\n    if (u->cleanup) {\n        ngx_http_lua_socket_udp_cleanup(u); /* it will clear u->cleanup */\n    }\n\n    return 0;\n}\n\n\nstatic void\nngx_http_lua_socket_dummy_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua udp socket dummy handler\");\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_udp_read(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u)\n{\n    ngx_connection_t            *c;\n    ngx_event_t                 *rev;\n    ssize_t                      n;\n\n    c = u->udp_connection.connection;\n    rev = c->read;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"lua udp socket read data: waiting: %d\", (int) u->waiting);\n\n    n = ngx_udp_recv(c,\n                     ngx_http_lua_socket_udp_buffer, u->recv_buf_size);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"lua udp recv returned %z\", n);\n\n    if (n >= 0) {\n        u->received = n;\n        ngx_http_lua_socket_udp_handle_success(r, u);\n        return NGX_OK;\n    }\n\n    if (n == NGX_ERROR) {\n        u->socket_errno = ngx_socket_errno;\n        ngx_http_lua_socket_udp_handle_error(r, u,\n                                             NGX_HTTP_LUA_SOCKET_FT_ERROR);\n        return NGX_ERROR;\n    }\n\n    /* n == NGX_AGAIN */\n\n#if 1\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        ngx_http_lua_socket_udp_handle_error(r, u,\n                                             NGX_HTTP_LUA_SOCKET_FT_ERROR);\n        return NGX_ERROR;\n    }\n#endif\n\n    if (rev->active) {\n        ngx_add_timer(rev, u->read_timeout);\n\n    } else if (rev->timer_set) {\n        ngx_del_timer(rev);\n    }\n\n    return NGX_AGAIN;\n}\n\n\nstatic void\nngx_http_lua_socket_udp_read_handler(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u)\n{\n    ngx_connection_t            *c;\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    c = u->udp_connection.connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua udp socket read handler\");\n\n    if (c->read->timedout) {\n        c->read->timedout = 0;\n\n        llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n        if (llcf->log_socket_errors) {\n            ngx_log_error(NGX_LOG_ERR, &u->udp_connection.log, 0,\n                          \"lua udp socket read timed out\");\n        }\n\n        ngx_http_lua_socket_udp_handle_error(r, u,\n                                             NGX_HTTP_LUA_SOCKET_FT_TIMEOUT);\n        return;\n    }\n\n#if 1\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n#endif\n\n    (void) ngx_http_lua_socket_udp_read(r, u);\n}\n\n\nstatic void\nngx_http_lua_socket_udp_handle_error(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u, ngx_uint_t ft_type)\n{\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua udp socket handle error\");\n\n    u->ft_type |= ft_type;\n\n#if 0\n    ngx_http_lua_socket_udp_finalize(r, u);\n#endif\n\n    u->read_event_handler = ngx_http_lua_socket_dummy_handler;\n\n    coctx = u->co_ctx;\n\n    if (coctx) {\n        coctx->cleanup = NULL;\n    }\n\n    if (u->waiting) {\n        u->waiting = 0;\n\n        ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n        if (ctx == NULL) {\n            return;\n        }\n\n        ctx->resume_handler = ngx_http_lua_socket_udp_resume;\n        ctx->cur_co_ctx = coctx;\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua udp socket waking up the current request\");\n\n        r->write_event_handler(r);\n    }\n}\n\n\nstatic void\nngx_http_lua_socket_udp_cleanup(void *data)\n{\n    ngx_http_lua_socket_udp_upstream_t  *u = data;\n\n    ngx_http_request_t  *r;\n\n    r = u->request;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"cleanup lua udp socket upstream request: \\\"%V\\\"\", &r->uri);\n\n    ngx_http_lua_socket_udp_finalize(r, u);\n}\n\n\nstatic void\nngx_http_lua_socket_udp_handler(ngx_event_t *ev)\n{\n    ngx_connection_t                *c;\n    ngx_http_request_t              *r;\n    ngx_http_log_ctx_t              *ctx;\n\n    ngx_http_lua_socket_udp_upstream_t  *u;\n\n    c = ev->data;\n    u = c->data;\n    r = u->request;\n    c = r->connection;\n\n    if (c->fd != (ngx_socket_t) -1) {  /* not a fake connection */\n        ctx = c->log->data;\n        ctx->current_request = r;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"lua udp socket handler for \\\"%V?%V\\\", wev %d\", &r->uri,\n                   &r->args, (int) ev->write);\n\n    u->read_event_handler(r, u);\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nstatic void\nngx_http_lua_socket_udp_handle_success(ngx_http_request_t *r,\n    ngx_http_lua_socket_udp_upstream_t *u)\n{\n    ngx_http_lua_ctx_t          *ctx;\n\n    u->read_event_handler = ngx_http_lua_socket_dummy_handler;\n\n    if (u->co_ctx) {\n        u->co_ctx->cleanup = NULL;\n    }\n\n    if (u->waiting) {\n        u->waiting = 0;\n\n        ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n        if (ctx == NULL) {\n            return;\n        }\n\n        ctx->resume_handler = ngx_http_lua_socket_udp_resume;\n        ctx->cur_co_ctx = u->co_ctx;\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua udp socket waking up the current request\");\n\n        r->write_event_handler(r);\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_lua_udp_connect(ngx_http_lua_udp_connection_t *uc, ngx_addr_t *local)\n{\n    int                rc;\n    ngx_int_t          event;\n    ngx_event_t       *rev, *wev;\n    ngx_socket_t       s;\n    ngx_connection_t  *c;\n\n    s = ngx_socket(uc->sockaddr->sa_family, SOCK_DGRAM, 0);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &uc->log, 0, \"UDP socket %d\", s);\n\n    if (s == (ngx_socket_t) -1) {\n        ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,\n                      ngx_socket_n \" failed\");\n\n        return NGX_ERROR;\n    }\n\n    c = ngx_get_connection(s, &uc->log);\n\n    if (c == NULL) {\n        if (ngx_close_socket(s) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,\n                          ngx_close_socket_n \"failed\");\n        }\n\n        return NGX_ERROR;\n    }\n\n    if (ngx_nonblocking(s) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,\n                      ngx_nonblocking_n \" failed\");\n\n        ngx_free_connection(c);\n\n        if (ngx_close_socket(s) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,\n                          ngx_close_socket_n \" failed\");\n        }\n\n        return NGX_ERROR;\n    }\n\n    rev = c->read;\n    wev = c->write;\n\n    rev->log = &uc->log;\n    wev->log = &uc->log;\n\n    uc->connection = c;\n\n    c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);\n\n#if (NGX_HTTP_LUA_HAVE_SO_PASSCRED)\n    if (uc->sockaddr->sa_family == AF_UNIX) {\n        struct sockaddr         addr;\n\n        addr.sa_family = AF_UNIX;\n\n        /* just to make valgrind happy */\n        ngx_memzero(addr.sa_data, sizeof(addr.sa_data));\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, &uc->log, 0, \"datagram unix \"\n                       \"domain socket autobind\");\n\n        if (bind(uc->connection->fd, &addr, sizeof(sa_family_t)) != 0) {\n            ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno,\n                          \"bind() failed\");\n\n            return NGX_ERROR;\n        }\n    }\n\n#endif\n\n    if (local != NULL) {\n        if (bind(s, local->sockaddr, local->socklen) == -1) {\n            ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno,\n                          \"bind(%V) failed\", &local->name);\n\n            return NGX_ERROR;\n        }\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &uc->log, 0,\n                   \"connect to %V, fd:%d #%d\", &uc->server, s, c->number);\n\n    rc = connect(s, uc->sockaddr, uc->socklen);\n\n    /* TODO: aio, iocp */\n\n    if (rc == -1) {\n        ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno,\n                      \"connect() failed\");\n\n        return NGX_ERROR;\n    }\n\n    /* UDP sockets are always ready to write */\n    wev->ready = 1;\n\n    if (ngx_add_event) {\n\n        event = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ?\n                    /* kqueue, epoll */                 NGX_CLEAR_EVENT:\n                    /* select, poll, /dev/poll */       NGX_LEVEL_EVENT;\n                    /* eventport event type has no meaning: oneshot only */\n\n        if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n    } else {\n        /* rtsig */\n\n        if (ngx_add_conn(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic int\nngx_http_lua_socket_udp_close(lua_State *L)\n{\n    ngx_http_request_t                  *r;\n    ngx_http_lua_socket_udp_upstream_t  *u;\n\n    if (lua_gettop(L) != 1) {\n        return luaL_error(L, \"expecting 1 argument \"\n                          \"(including the object) but seen %d\", lua_gettop(L));\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);\n    u = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    if (u == NULL || u->udp_connection.connection == NULL) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"closed\");\n        return 2;\n    }\n\n    if (u->request != r) {\n        return luaL_error(L, \"bad request\");\n    }\n\n    if (u->waiting) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"socket busy\");\n        return 2;\n    }\n\n    ngx_http_lua_socket_udp_finalize(r, u);\n\n    lua_pushinteger(L, 1);\n    return 1;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_socket_udp_resume(ngx_http_request_t *r)\n{\n    int                          nret;\n    lua_State                   *vm;\n    ngx_int_t                    rc;\n    ngx_uint_t                   nreqs;\n    ngx_connection_t            *c;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    ngx_http_lua_socket_udp_upstream_t      *u;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->resume_handler = ngx_http_lua_wev_handler;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua udp operation done, resuming lua thread\");\n\n    coctx = ctx->cur_co_ctx;\n\n#if 0\n    ngx_http_lua_probe_info(\"udp resume\");\n#endif\n\n    u = coctx->data;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua udp socket calling prepare retvals handler %p, \"\n                   \"u:%p\", u->prepare_retvals, u);\n\n    nret = u->prepare_retvals(r, u, ctx->cur_co_ctx->co);\n    if (nret == NGX_AGAIN) {\n        return NGX_DONE;\n    }\n\n    c = r->connection;\n    vm = ngx_http_lua_get_lua_vm(r, ctx);\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(vm, r, ctx, nret);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run thread returned %d\", rc);\n\n    if (rc == NGX_AGAIN) {\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (ctx->entered_content_phase) {\n        ngx_http_lua_finalize_request(r, rc);\n        return NGX_DONE;\n    }\n\n    return rc;\n}\n\n\nstatic void\nngx_http_lua_udp_resolve_cleanup(void *data)\n{\n    ngx_resolver_ctx_t                      *rctx;\n    ngx_http_lua_socket_udp_upstream_t      *u;\n    ngx_http_lua_co_ctx_t                   *coctx = data;\n\n    u = coctx->data;\n    if (u == NULL) {\n        return;\n    }\n\n    rctx = u->resolved->ctx;\n    if (rctx == NULL) {\n        return;\n    }\n\n    /* postpone free the rctx in the handler */\n    rctx->handler = ngx_resolve_name_done;\n}\n\n\nstatic void\nngx_http_lua_udp_socket_cleanup(void *data)\n{\n    ngx_http_lua_socket_udp_upstream_t      *u;\n    ngx_http_lua_co_ctx_t                   *coctx = data;\n\n    u = coctx->data;\n    if (u == NULL) {\n        return;\n    }\n\n    if (u->request == NULL) {\n        return;\n    }\n\n    ngx_http_lua_socket_udp_finalize(u->request, u);\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_socket_udp.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_SOCKET_UDP_H_INCLUDED_\n#define _NGX_HTTP_LUA_SOCKET_UDP_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\ntypedef struct ngx_http_lua_socket_udp_upstream_s\n    ngx_http_lua_socket_udp_upstream_t;\n\n\ntypedef\n    int (*ngx_http_lua_socket_udp_retval_handler)(ngx_http_request_t *r,\n        ngx_http_lua_socket_udp_upstream_t *u, lua_State *L);\n\n\ntypedef void (*ngx_http_lua_socket_udp_upstream_handler_pt)\n    (ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u);\n\n\ntypedef struct {\n    ngx_connection_t         *connection;\n    struct sockaddr          *sockaddr;\n    socklen_t                 socklen;\n    ngx_str_t                 server;\n    ngx_log_t                 log;\n} ngx_http_lua_udp_connection_t;\n\n\nstruct ngx_http_lua_socket_udp_upstream_s {\n    ngx_http_lua_socket_udp_retval_handler          prepare_retvals;\n    ngx_http_lua_socket_udp_upstream_handler_pt     read_event_handler;\n\n    ngx_http_lua_loc_conf_t         *conf;\n    ngx_http_cleanup_pt             *cleanup;\n    ngx_http_request_t              *request;\n    ngx_http_lua_udp_connection_t    udp_connection;\n\n    ngx_addr_t                      *local;\n\n    ngx_msec_t                       read_timeout;\n\n    ngx_http_upstream_resolved_t    *resolved;\n\n    ngx_str_t                        host;\n    in_port_t                        port;\n    ngx_uint_t                       ft_type;\n    ngx_err_t                        socket_errno;\n    size_t                           received; /* for receive */\n    size_t                           recv_buf_size;\n\n    ngx_http_lua_co_ctx_t           *co_ctx;\n\n    unsigned                         waiting:1; /* :1 */\n};\n\n\nvoid ngx_http_lua_inject_socket_udp_api(ngx_log_t *log, lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_SOCKET_UDP_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_ssl.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n#if (NGX_HTTP_SSL)\n\n#include \"ngx_http_lua_ssl.h\"\n\n\nint ngx_http_lua_ssl_ctx_index = -1;\nint ngx_http_lua_ssl_key_log_index = -1;\n\n\nngx_int_t\nngx_http_lua_ssl_init(ngx_log_t *log)\n{\n    if (ngx_http_lua_ssl_ctx_index == -1) {\n        ngx_http_lua_ssl_ctx_index = SSL_get_ex_new_index(0, NULL, NULL,\n                                                          NULL, NULL);\n\n        if (ngx_http_lua_ssl_ctx_index == -1) {\n            ngx_ssl_error(NGX_LOG_ALERT, log, 0,\n                          \"lua: SSL_get_ex_new_index() for ctx failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    if (ngx_http_lua_ssl_key_log_index == -1) {\n        ngx_http_lua_ssl_key_log_index = SSL_get_ex_new_index(0, NULL, NULL,\n                                                              NULL, NULL);\n\n        if (ngx_http_lua_ssl_key_log_index == -1) {\n            ngx_ssl_error(NGX_LOG_ALERT, log, 0,\n                          \"lua: SSL_get_ex_new_index() for key log failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nngx_ssl_conn_t *\nngx_http_lua_ffi_get_upstream_ssl_pointer(ngx_http_request_t *r,\n    const char **err)\n{\n    ngx_connection_t  *c;\n\n    if (r == NULL) {\n        *err = \"bad request\";\n        return NULL;\n    }\n\n    if (r->upstream == NULL) {\n        *err = \"bad upstream\";\n        return NULL;\n    }\n\n    if (r->upstream->peer.connection == NULL) {\n        *err = \"bad peer connection\";\n        return NULL;\n    }\n\n    c = r->upstream->peer.connection;\n\n    if (c->ssl == NULL || c->ssl->connection == NULL) {\n        *err = \"not ssl connection\";\n        return NULL;\n    }\n\n    return c->ssl->connection;\n}\n\n\n#endif /* NGX_HTTP_SSL */\n"
  },
  {
    "path": "src/ngx_http_lua_ssl.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_SSL_H_INCLUDED_\n#define _NGX_HTTP_LUA_SSL_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\n#if (NGX_HTTP_SSL)\n\n\ntypedef struct {\n    ngx_connection_t        *connection; /* original true connection */\n    ngx_http_request_t      *request;\n    ngx_pool_cleanup_pt     *cleanup;\n\n    ngx_ssl_session_t       *session;    /* return value for openssl's\n                                          * session_get_cb */\n\n    ngx_str_t                session_id;\n\n#if HAVE_LUA_PROXY_SSL\n    X509_STORE_CTX          *x509_store;\n    ngx_pool_t              *pool;\n#endif\n\n    int                      exit_code;  /* exit code for openssl's\n                                            set_client_hello_cb or\n                                            set_cert_cb callback or\n                                            SSL_CTX_set_cert_verify_callback */\n\n    int                      ctx_ref;  /*  reference to anchor\n                                           request ctx data in lua\n                                           registry */\n\n#if HAVE_LUA_PROXY_SSL\n    /* same size as count field of ngx_http_request_t */\n    unsigned                 original_request_count:16;\n#endif\n    unsigned                 done:1;\n    unsigned                 aborted:1;\n\n    unsigned                 entered_client_hello_handler:1;\n    unsigned                 entered_cert_handler:1;\n    unsigned                 entered_sess_fetch_handler:1;\n#if HAVE_LUA_PROXY_SSL\n    unsigned                 entered_proxy_ssl_cert_handler:1;\n    unsigned                 entered_proxy_ssl_verify_handler:1;\n#endif\n} ngx_http_lua_ssl_ctx_t;\n\n\ntypedef struct {\n    ngx_ssl_t     *ssl;\n    ngx_fd_t       fd;\n    ngx_str_t      name;\n} ngx_http_lua_ssl_key_log_t;\n\n\nngx_int_t ngx_http_lua_ssl_init(ngx_log_t *log);\n\n\nextern int ngx_http_lua_ssl_ctx_index;\nextern int ngx_http_lua_ssl_key_log_index;\n\n\n#endif\n\n\n#endif  /* _NGX_HTTP_LUA_SSL_H_INCLUDED_ */\n"
  },
  {
    "path": "src/ngx_http_lua_ssl_certby.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#if (NGX_HTTP_SSL)\n\n\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_initworkerby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_ssl_module.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_ssl_certby.h\"\n#include \"ngx_http_lua_directive.h\"\n#include \"ngx_http_lua_ssl.h\"\n\n\nenum {\n    NGX_HTTP_LUA_ADDR_TYPE_UNIX  = 0,\n    NGX_HTTP_LUA_ADDR_TYPE_INET  = 1,\n    NGX_HTTP_LUA_ADDR_TYPE_INET6 = 2,\n};\n\n\nstatic void ngx_http_lua_ssl_cert_done(void *data);\nstatic void ngx_http_lua_ssl_cert_aborted(void *data);\nstatic u_char *ngx_http_lua_log_ssl_cert_error(ngx_log_t *log, u_char *buf,\n    size_t len);\nstatic ngx_int_t ngx_http_lua_ssl_cert_by_chunk(lua_State *L,\n    ngx_http_request_t *r);\n\n#ifndef OPENSSL_IS_BORINGSSL\n#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER > 0x101010afL\nstatic int ngx_http_lua_is_grease_cipher(uint16_t cipher_id);\n#endif\n#endif\n\n\nngx_int_t\nngx_http_lua_ssl_cert_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L,\n                                     lscf->srv.ssl_cert_src.data,\n                                     &lscf->srv.ssl_cert_src_ref,\n                                     lscf->srv.ssl_cert_src_key);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_ssl_cert_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_ssl_cert_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       lscf->srv.ssl_cert_src.data,\n                                       lscf->srv.ssl_cert_src.len,\n                                       &lscf->srv.ssl_cert_src_ref,\n                                       lscf->srv.ssl_cert_src_key,\n                                       (const char *)\n                                       lscf->srv.ssl_cert_chunkname);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_ssl_cert_by_chunk(L, r);\n}\n\n\nchar *\nngx_http_lua_ssl_cert_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_ssl_cert_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_ssl_cert_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n#if OPENSSL_VERSION_NUMBER < 0x1000205fL\n\n    ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                  \"at least OpenSSL 1.0.2e required but found \"\n                  OPENSSL_VERSION_TEXT);\n\n    return NGX_CONF_ERROR;\n\n#else\n\n    size_t                       chunkname_len;\n    u_char                      *chunkname;\n    u_char                      *cache_key = NULL;\n    u_char                      *name;\n    ngx_str_t                   *value;\n    ngx_http_lua_srv_conf_t     *lscf = conf;\n\n    /*  must specify a concrete handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (lscf->srv.ssl_cert_handler) {\n        return \"is duplicate\";\n    }\n\n    if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    lscf->srv.ssl_cert_handler = (ngx_http_lua_srv_conf_handler_pt) cmd->post;\n\n    if (cmd->post == ngx_http_lua_ssl_cert_handler_file) {\n        /* Lua code in an external file */\n        name = ngx_http_lua_rebase_path(cf->pool, value[1].data,\n                                        value[1].len);\n        if (name == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                    value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        lscf->srv.ssl_cert_src.data = name;\n        lscf->srv.ssl_cert_src.len = ngx_strlen(name);\n\n    } else {\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf,\n                                                     \"ssl_certificate_by_lua\",\n                                                     value[1].data,\n                                                     value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"ssl_certificate_by_lua\",\n                          sizeof(\"ssl_certificate_by_lua\") - 1, &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        lscf->srv.ssl_cert_src = value[1];\n        lscf->srv.ssl_cert_chunkname = chunkname;\n    }\n\n    lscf->srv.ssl_cert_src_key = cache_key;\n\n    return NGX_CONF_OK;\n\n#endif  /* OPENSSL_VERSION_NUMBER < 0x1000205fL */\n}\n\n\nint\nngx_http_lua_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data)\n{\n    lua_State                       *L;\n    ngx_int_t                        rc;\n    ngx_connection_t                *c, *fc;\n    ngx_http_request_t              *r = NULL;\n    ngx_pool_cleanup_t              *cln;\n    ngx_http_connection_t           *hc;\n    ngx_http_lua_srv_conf_t         *lscf;\n    ngx_http_core_loc_conf_t        *clcf;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n    ngx_http_core_srv_conf_t        *cscf;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"ssl cert: connection reusable: %ud\", c->reusable);\n\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n\n    dd(\"ssl cert handler, cert-ctx=%p\", cctx);\n\n    if (cctx && cctx->entered_cert_handler) {\n        /* not the first time */\n\n        if (cctx->done) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"ssl_certificate_by_lua: cert cb exit code: %d\",\n                           cctx->exit_code);\n\n            dd(\"lua ssl cert done, finally\");\n            return cctx->exit_code;\n        }\n\n        return -1;\n    }\n\n    dd(\"first time\");\n\n#if (nginx_version < 1017009)\n    ngx_reusable_connection(c, 0);\n#endif\n\n    hc = c->data;\n\n    fc = ngx_http_lua_create_fake_connection(NULL);\n    if (fc == NULL) {\n        goto failed;\n    }\n\n    fc->log->handler = ngx_http_lua_log_ssl_cert_error;\n    fc->log->data = fc;\n\n    fc->addr_text = c->addr_text;\n    fc->listening = c->listening;\n\n    r = ngx_http_lua_create_fake_request(fc);\n    if (r == NULL) {\n        goto failed;\n    }\n\n    r->main_conf = hc->conf_ctx->main_conf;\n    r->srv_conf = hc->conf_ctx->srv_conf;\n    r->loc_conf = hc->conf_ctx->loc_conf;\n\n    fc->log->file = c->log->file;\n    fc->log->log_level = c->log->log_level;\n    fc->ssl = c->ssl;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n#if (nginx_version >= 1009000)\n    ngx_set_connection_log(fc, clcf->error_log);\n\n#else\n    ngx_http_set_connection_log(fc, clcf->error_log);\n#endif\n\n    if (cctx == NULL) {\n        cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t));\n        if (cctx == NULL) {\n            goto failed;  /* error */\n        }\n\n        cctx->ctx_ref = LUA_NOREF;\n    }\n\n    cctx->exit_code = 1;  /* successful by default */\n    cctx->connection = c;\n    cctx->request = r;\n    cctx->entered_cert_handler = 1;\n    cctx->done = 0;\n\n    dd(\"setting cctx\");\n\n    if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index, cctx)\n        == 0)\n    {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"SSL_set_ex_data() failed\");\n        goto failed;\n    }\n\n    lscf = ngx_http_get_module_srv_conf(r, ngx_http_lua_module);\n\n    /* TODO honor lua_code_cache off */\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    c->log->action = \"loading SSL certificate by lua\";\n\n    if (lscf->srv.ssl_cert_handler == NULL) {\n        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                      \"no ssl_certificate_by_lua* defined in \"\n                      \"server %V\", &cscf->server_name);\n\n        goto failed;\n    }\n\n    rc = lscf->srv.ssl_cert_handler(r, lscf, L);\n\n    if (rc >= NGX_OK || rc == NGX_ERROR) {\n        cctx->done = 1;\n\n        if (cctx->cleanup) {\n            *cctx->cleanup = NULL;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"ssl_certificate_by_lua: handler return value: %i, \"\n                       \"cert cb exit code: %d\", rc, cctx->exit_code);\n\n        c->log->action = \"SSL handshaking\";\n        return cctx->exit_code;\n    }\n\n    /* rc == NGX_DONE */\n\n    cln = ngx_pool_cleanup_add(fc->pool, 0);\n    if (cln == NULL) {\n        goto failed;\n    }\n\n    cln->handler = ngx_http_lua_ssl_cert_done;\n    cln->data = cctx;\n\n    if (cctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(c->pool, 0);\n        if (cln == NULL) {\n            goto failed;\n        }\n\n        cln->data = cctx;\n        cctx->cleanup = &cln->handler;\n    }\n\n    *cctx->cleanup = ngx_http_lua_ssl_cert_aborted;\n\n    return -1;\n\nfailed:\n\n    if (r && r->pool) {\n        ngx_http_lua_free_fake_request(r);\n    }\n\n    if (fc) {\n        ngx_http_lua_close_fake_connection(fc);\n    }\n\n    return 0;\n}\n\n\nstatic void\nngx_http_lua_ssl_cert_done(void *data)\n{\n    ngx_connection_t        *c;\n    ngx_http_lua_ssl_ctx_t  *cctx = data;\n\n    dd(\"lua ssl cert done\");\n\n    if (cctx->aborted) {\n        return;\n    }\n\n    ngx_http_lua_assert(cctx->done == 0);\n\n    cctx->done = 1;\n\n    if (cctx->cleanup) {\n        *cctx->cleanup = NULL;\n    }\n\n    c = cctx->connection;\n\n    c->log->action = \"SSL handshaking\";\n\n    ngx_post_event(c->write, &ngx_posted_events);\n\n#if (HAVE_QUIC_SSL_LUA_YIELD_PATCH && NGX_HTTP_V3)\n#   if OPENSSL_VERSION_NUMBER >= 0x1000205fL\n#       if (NGX_QUIC_OPENSSL_COMPAT || NGX_QUIC_OPENSSL_API)\n    ngx_http_lua_resume_quic_ssl_handshake(c);\n#       endif\n#   endif\n#endif\n}\n\n\nstatic void\nngx_http_lua_ssl_cert_aborted(void *data)\n{\n    ngx_http_lua_ssl_ctx_t      *cctx = data;\n\n    dd(\"lua ssl cert aborted\");\n\n    if (cctx->done) {\n        /* completed successfully already */\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0,\n                   \"ssl_certificate_by_lua: cert cb aborted\");\n\n    cctx->aborted = 1;\n    cctx->request->connection->ssl = NULL;\n\n    ngx_http_lua_finalize_fake_request(cctx->request, NGX_ERROR);\n}\n\n\nstatic u_char *\nngx_http_lua_log_ssl_cert_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char              *p;\n    ngx_connection_t    *c;\n\n    if (log->action) {\n        p = ngx_snprintf(buf, len, \" while %s\", log->action);\n        len -= p - buf;\n        buf = p;\n    }\n\n    p = ngx_snprintf(buf, len, \", context: ssl_certificate_by_lua*\");\n    len -= p - buf;\n    buf = p;\n\n    c = log->data;\n\n    if (c && c->addr_text.len) {\n        p = ngx_snprintf(buf, len, \", client: %V\", &c->addr_text);\n        len -= p - buf;\n        buf = p;\n    }\n\n    if (c && c->listening && c->listening->addr_text.len) {\n        p = ngx_snprintf(buf, len, \", server: %V\", &c->listening->addr_text);\n        /* len -= p - buf; */\n        buf = p;\n    }\n\n    return buf;\n}\n\n\n#ifndef OPENSSL_IS_BORINGSSL\n#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER > 0x101010afL\nstatic int\nngx_http_lua_is_grease_cipher(uint16_t cipher_id)\n{\n    /* GREASE values follow pattern: 0x?A?A where ? can be any hex digit */\n    /* and both ? must be the same */\n    /* Check if both bytes follow ?A pattern and high nibbles match */\n    return (cipher_id & 0x0F0F) == 0x0A0A;\n}\n#endif\n#endif\n\n\nstatic ngx_int_t\nngx_http_lua_ssl_cert_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    int                      co_ref;\n    ngx_int_t                rc;\n    lua_State               *co;\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_pool_cleanup_t      *cln;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            rc = NGX_ERROR;\n            ngx_http_lua_finalize_request(r, rc);\n            return rc;\n        }\n\n    } else {\n        dd(\"reset ctx\");\n        ngx_http_lua_reset_ctx(r, L, ctx);\n    }\n\n    ctx->entered_content_phase = 1;\n\n    /*  {{{ new coroutine to handle request */\n    co = ngx_http_lua_new_thread(r, L, &co_ref);\n\n    if (co == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"lua: failed to create new coroutine to handle request\");\n\n        rc = NGX_ERROR;\n        ngx_http_lua_finalize_request(r, rc);\n        return rc;\n    }\n\n    /*  move code closure to new coroutine */\n    lua_xmove(L, co, 1);\n\n#ifndef OPENRESTY_LUAJIT\n    /*  set closure's env table to new coroutine's globals table */\n    ngx_http_lua_get_globals_table(co);\n    lua_setfenv(co, -2);\n#endif\n\n    /* save nginx request in coroutine globals table */\n    ngx_http_lua_set_req(co, r);\n\n    ctx->cur_co_ctx = &ctx->entry_co_ctx;\n    ctx->cur_co_ctx->co = co;\n    ctx->cur_co_ctx->co_ref = co_ref;\n#ifdef NGX_LUA_USE_ASSERT\n    ctx->cur_co_ctx->co_top = 1;\n#endif\n\n    ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);\n\n    /* register request cleanup hooks */\n    if (ctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n        if (cln == NULL) {\n            rc = NGX_ERROR;\n            ngx_http_lua_finalize_request(r, rc);\n            return rc;\n        }\n\n        cln->handler = ngx_http_lua_request_cleanup_handler;\n        cln->data = ctx;\n        ctx->cleanup = &cln->handler;\n    }\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_SSL_CERT;\n\n    rc = ngx_http_lua_run_thread(L, r, ctx, 0);\n\n    if (rc == NGX_ERROR || rc >= NGX_OK) {\n        /* do nothing */\n\n    } else if (rc == NGX_AGAIN) {\n        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0);\n\n    } else if (rc == NGX_DONE) {\n        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 1);\n\n    } else {\n        rc = NGX_OK;\n    }\n\n    ngx_http_lua_finalize_request(r, rc);\n    return rc;\n}\n\n\nint\nngx_http_lua_ffi_ssl_get_tls1_version(ngx_http_request_t *r, char **err)\n{\n    ngx_ssl_conn_t    *ssl_conn;\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    dd(\"tls1 ver: %d\", SSL_version(ssl_conn));\n\n    return SSL_version(ssl_conn);\n}\n\n\nint\nngx_http_lua_ffi_ssl_clear_certs(ngx_http_request_t *r, char **err)\n{\n#ifdef LIBRESSL_VERSION_NUMBER\n\n    *err = \"LibreSSL not supported\";\n    return NGX_ERROR;\n\n#else\n\n#   if OPENSSL_VERSION_NUMBER < 0x1000205fL\n\n    *err = \"at least OpenSSL 1.0.2e required but found \" OPENSSL_VERSION_TEXT;\n    return NGX_ERROR;\n\n#   else\n\n    ngx_ssl_conn_t    *ssl_conn;\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    SSL_certs_clear(ssl_conn);\n    return NGX_OK;\n\n#   endif  /* OPENSSL_VERSION_NUMBER < 0x1000205fL */\n#endif\n}\n\n\nint\nngx_http_lua_ffi_ssl_set_der_certificate(ngx_http_request_t *r,\n    const char *data, size_t len, char **err)\n{\n#ifdef LIBRESSL_VERSION_NUMBER\n\n    *err = \"LibreSSL not supported\";\n    return NGX_ERROR;\n\n#else\n\n#   if OPENSSL_VERSION_NUMBER < 0x1000205fL\n\n    *err = \"at least OpenSSL 1.0.2e required but found \" OPENSSL_VERSION_TEXT;\n    return NGX_ERROR;\n\n#   else\n\n    BIO               *bio = NULL;\n    X509              *x509 = NULL;\n    ngx_ssl_conn_t    *ssl_conn;\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    bio = BIO_new_mem_buf((char *) data, len);\n    if (bio == NULL) {\n        *err = \"BIO_new_mem_buf() failed\";\n        goto failed;\n    }\n\n    x509 = d2i_X509_bio(bio, NULL);\n    if (x509 == NULL) {\n        *err = \"d2i_X509_bio() failed\";\n        goto failed;\n    }\n\n    if (SSL_use_certificate(ssl_conn, x509) == 0) {\n        *err = \"SSL_use_certificate() failed\";\n        goto failed;\n    }\n\n#if 0\n    if (SSL_set_ex_data(ssl_conn, ngx_ssl_certificate_index, x509) == 0) {\n        *err = \"SSL_set_ex_data() failed\";\n        goto failed;\n    }\n#endif\n\n    X509_free(x509);\n    x509 = NULL;\n\n    /* read rest of the chain */\n\n    while (!BIO_eof(bio)) {\n\n        x509 = d2i_X509_bio(bio, NULL);\n        if (x509 == NULL) {\n            *err = \"d2i_X509_bio() failed\";\n            goto failed;\n        }\n\n        if (SSL_add0_chain_cert(ssl_conn, x509) == 0) {\n            *err = \"SSL_add0_chain_cert() failed\";\n            goto failed;\n        }\n    }\n\n    BIO_free(bio);\n\n    *err = NULL;\n    return NGX_OK;\n\nfailed:\n\n    if (bio) {\n        BIO_free(bio);\n    }\n\n    if (x509) {\n        X509_free(x509);\n    }\n\n    ERR_clear_error();\n\n    return NGX_ERROR;\n\n#   endif  /* OPENSSL_VERSION_NUMBER < 0x1000205fL */\n#endif\n}\n\n\nint\nngx_http_lua_ffi_ssl_set_der_private_key(ngx_http_request_t *r,\n    const char *data, size_t len, char **err)\n{\n    BIO               *bio = NULL;\n    EVP_PKEY          *pkey = NULL;\n    ngx_ssl_conn_t    *ssl_conn;\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    bio = BIO_new_mem_buf((char *) data, len);\n    if (bio == NULL) {\n        *err = \"BIO_new_mem_buf() failed\";\n        goto failed;\n    }\n\n    pkey = d2i_PrivateKey_bio(bio, NULL);\n    if (pkey == NULL) {\n        *err = \"d2i_PrivateKey_bio() failed\";\n        goto failed;\n    }\n\n    if (SSL_use_PrivateKey(ssl_conn, pkey) == 0) {\n        *err = \"SSL_use_PrivateKey() failed\";\n        goto failed;\n    }\n\n    EVP_PKEY_free(pkey);\n    BIO_free(bio);\n\n    return NGX_OK;\n\nfailed:\n\n    if (pkey) {\n        EVP_PKEY_free(pkey);\n    }\n\n    if (bio) {\n        BIO_free(bio);\n    }\n\n    ERR_clear_error();\n\n    return NGX_ERROR;\n}\n\n\nint\nngx_http_lua_ffi_ssl_raw_server_addr(ngx_http_request_t *r, char **addr,\n    size_t *addrlen, int *addrtype, char **err)\n{\n#if (NGX_HAVE_UNIX_DOMAIN)\n    struct sockaddr_un   *saun;\n#endif\n    ngx_ssl_conn_t       *ssl_conn;\n    ngx_connection_t     *c;\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin6;\n#endif\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {\n        return 0;\n    }\n\n    switch (c->local_sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) c->local_sockaddr;\n        *addrlen = 16;\n        *addr = (char *) &sin6->sin6_addr.s6_addr;\n        *addrtype = NGX_HTTP_LUA_ADDR_TYPE_INET6;\n\n        break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    case AF_UNIX:\n        saun = (struct sockaddr_un *) c->local_sockaddr;\n\n        /* on Linux sockaddr might not include sun_path at all */\n        if (c->local_socklen <= (socklen_t)\n            offsetof(struct sockaddr_un, sun_path))\n        {\n            *addr = \"\";\n            *addrlen = 0;\n\n        } else {\n            *addr = saun->sun_path;\n            *addrlen = ngx_strlen(saun->sun_path);\n        }\n\n        *addrtype = NGX_HTTP_LUA_ADDR_TYPE_UNIX;\n        break;\n#endif\n\n    default: /* AF_INET */\n        sin = (struct sockaddr_in *) c->local_sockaddr;\n        *addr = (char *) &sin->sin_addr.s_addr;\n        *addrlen = 4;\n        *addrtype = NGX_HTTP_LUA_ADDR_TYPE_INET;\n        break;\n    }\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_req_shared_ssl_ciphers(ngx_http_request_t *r,\n    uint16_t *ciphers, uint16_t *nciphers, int filter_grease, char **err)\n{\n#ifdef OPENSSL_IS_BORINGSSL\n\n    *err = \"BoringSSL is not supported for SSL cipher operations\";\n    return NGX_ERROR;\n\n#elif OPENSSL_VERSION_NUMBER < 0x101010afL\n    *err = \"OpenSSL < 1.1.1a is not supported for SSL cipher operations\";\n    return NGX_ERROR;\n#else\n\n    ngx_ssl_conn_t         *ssl_conn;\n    STACK_OF(SSL_CIPHER)   *sk, *ck;\n    int                     sn, cn, i, n;\n    uint16_t                cipher;\n\n    if (r == NULL || r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    sk = SSL_get1_supported_ciphers(ssl_conn);\n    ck = SSL_get_client_ciphers(ssl_conn);\n    sn = sk_SSL_CIPHER_num(sk);\n    cn = sk_SSL_CIPHER_num(ck);\n\n    if (sn > *nciphers) {\n        *err = \"buffer too small\";\n        *nciphers = 0;\n        sk_SSL_CIPHER_free(sk);\n        return NGX_ERROR;\n    }\n\n    for (*nciphers = 0, i = 0; i < sn; i++) {\n        cipher = SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(sk, i));\n\n        /* Skip GREASE ciphers if filtering is enabled */\n        if (filter_grease && ngx_http_lua_is_grease_cipher(cipher)) {\n            continue;\n        }\n\n        for (n = 0; n < cn; n++) {\n            if (SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(ck, n))\n                == cipher)\n            {\n                ciphers[(*nciphers)++] = cipher;\n                break;\n            }\n        }\n    }\n\n    sk_SSL_CIPHER_free(sk);\n\n    return NGX_OK;\n#endif\n\n}\n\n\nint\nngx_http_lua_ffi_ssl_server_name(ngx_http_request_t *r, char **name,\n    size_t *namelen, char **err)\n{\n    ngx_ssl_conn_t          *ssl_conn;\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n\n    *name = (char *) SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);\n\n    if (*name) {\n        *namelen = ngx_strlen(*name);\n        return NGX_OK;\n    }\n\n    return NGX_DECLINED;\n\n#else\n\n    *err = \"no TLS extension support\";\n    return NGX_ERROR;\n\n#endif\n}\n\n\nint\nngx_http_lua_ffi_ssl_server_port(ngx_http_request_t *r,\n    unsigned short *server_port, char **err)\n{\n    ngx_ssl_conn_t          *ssl_conn;\n    ngx_connection_t        *c;\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    switch (c->local_sockaddr->sa_family) {\n\n    case AF_UNIX:\n        *err = \"unix domain has no port\";\n        return NGX_ERROR;\n\n    default:\n        *server_port = (unsigned short) ngx_inet_get_port(c->local_sockaddr);\n        return NGX_OK;\n    }\n}\n\n\nint\nngx_http_lua_ffi_ssl_raw_client_addr(ngx_http_request_t *r, char **addr,\n    size_t *addrlen, int *addrtype, char **err)\n{\n#if (NGX_HAVE_UNIX_DOMAIN)\n    struct sockaddr_un  *saun;\n#endif\n    ngx_ssl_conn_t      *ssl_conn;\n    ngx_connection_t    *c;\n    struct sockaddr_in  *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6 *sin6;\n#endif\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    switch (c->sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) c->sockaddr;\n        *addrlen = 16;\n        *addr = (char *) &sin6->sin6_addr.s6_addr;\n        *addrtype = NGX_HTTP_LUA_ADDR_TYPE_INET6;\n\n        break;\n#endif\n\n# if (NGX_HAVE_UNIX_DOMAIN)\n    case AF_UNIX:\n        saun = (struct sockaddr_un *)c->sockaddr;\n        /* on Linux sockaddr might not include sun_path at all */\n        if (c->socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)) {\n            *addr = \"\";\n            *addrlen = 0;\n\n        } else {\n            *addr = saun->sun_path;\n            *addrlen = ngx_strlen(saun->sun_path);\n        }\n\n        *addrtype = NGX_HTTP_LUA_ADDR_TYPE_UNIX;\n        break;\n#endif\n\n    default: /* AF_INET */\n        sin = (struct sockaddr_in *) c->sockaddr;\n        *addr = (char *) &sin->sin_addr.s_addr;\n        *addrlen = 4;\n        *addrtype = NGX_HTTP_LUA_ADDR_TYPE_INET;\n        break;\n    }\n\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_cert_pem_to_der(const u_char *pem, size_t pem_len, u_char *der,\n    char **err)\n{\n    int       total, len;\n    BIO      *bio;\n    X509     *x509;\n    u_long    n;\n\n    bio = BIO_new_mem_buf((char *) pem, (int) pem_len);\n    if (bio == NULL) {\n        *err = \"BIO_new_mem_buf() failed\";\n        ERR_clear_error();\n        return NGX_ERROR;\n    }\n\n    x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);\n    if (x509 == NULL) {\n        *err = \"PEM_read_bio_X509_AUX() failed\";\n        BIO_free(bio);\n        ERR_clear_error();\n        return NGX_ERROR;\n    }\n\n    total = i2d_X509(x509, &der);\n    if (total < 0) {\n        *err = \"i2d_X509() failed\";\n        X509_free(x509);\n        BIO_free(bio);\n        ERR_clear_error();\n        return NGX_ERROR;\n    }\n\n    X509_free(x509);\n\n    /* read rest of the chain */\n\n    for ( ;; ) {\n\n        x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);\n        if (x509 == NULL) {\n            n = ERR_peek_last_error();\n\n            if (ERR_GET_LIB(n) == ERR_LIB_PEM\n                && ERR_GET_REASON(n) == PEM_R_NO_START_LINE)\n            {\n                /* end of file */\n                ERR_clear_error();\n                break;\n            }\n\n            /* some real error */\n\n            *err = \"PEM_read_bio_X509() failed\";\n            BIO_free(bio);\n            ERR_clear_error();\n            return NGX_ERROR;\n        }\n\n        len = i2d_X509(x509, &der);\n        if (len < 0) {\n            *err = \"i2d_X509() failed\";\n            X509_free(x509);\n            BIO_free(bio);\n            ERR_clear_error();\n            return NGX_ERROR;\n        }\n\n        total += len;\n\n        X509_free(x509);\n    }\n\n    BIO_free(bio);\n\n    return total;\n}\n\n\nint\nngx_http_lua_ffi_priv_key_pem_to_der(const u_char *pem, size_t pem_len,\n    const u_char *passphrase, u_char *der, char **err)\n{\n    int          len;\n    BIO         *in;\n    EVP_PKEY    *pkey;\n\n    in = BIO_new_mem_buf((char *) pem, (int) pem_len);\n    if (in == NULL) {\n        *err = \"BIO_new_mem_buf() failed\";\n        ERR_clear_error();\n        return NGX_ERROR;\n    }\n\n    pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (void *) passphrase);\n    if (pkey == NULL) {\n        BIO_free(in);\n        *err = \"PEM_read_bio_PrivateKey() failed\";\n        ERR_clear_error();\n        return NGX_ERROR;\n    }\n\n    BIO_free(in);\n\n    len = i2d_PrivateKey(pkey, &der);\n    if (len < 0) {\n        EVP_PKEY_free(pkey);\n        *err = \"i2d_PrivateKey() failed\";\n        ERR_clear_error();\n        return NGX_ERROR;\n    }\n\n    EVP_PKEY_free(pkey);\n\n    return len;\n}\n\n\nvoid *\nngx_http_lua_ffi_parse_pem_cert(const u_char *pem, size_t pem_len,\n    char **err)\n{\n    BIO             *bio;\n    X509            *x509;\n    u_long           n;\n    STACK_OF(X509)  *chain;\n\n    bio = BIO_new_mem_buf((char *) pem, (int) pem_len);\n    if (bio == NULL) {\n        *err = \"BIO_new_mem_buf() failed\";\n        ERR_clear_error();\n        return NULL;\n    }\n\n    x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);\n    if (x509 == NULL) {\n        *err = \"PEM_read_bio_X509_AUX() failed\";\n        BIO_free(bio);\n        ERR_clear_error();\n        return NULL;\n    }\n\n    chain = sk_X509_new_null();\n    if (chain == NULL) {\n        *err = \"sk_X509_new_null() failed\";\n        X509_free(x509);\n        BIO_free(bio);\n        ERR_clear_error();\n        return NULL;\n    }\n\n    if (sk_X509_push(chain, x509) == 0) {\n        *err = \"sk_X509_push() failed\";\n        sk_X509_free(chain);\n        X509_free(x509);\n        BIO_free(bio);\n        ERR_clear_error();\n        return NULL;\n    }\n\n    /* read rest of the chain */\n\n    for ( ;; ) {\n\n        x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);\n        if (x509 == NULL) {\n            n = ERR_peek_last_error();\n\n            if (ERR_GET_LIB(n) == ERR_LIB_PEM\n                && ERR_GET_REASON(n) == PEM_R_NO_START_LINE)\n            {\n                /* end of file */\n                ERR_clear_error();\n                break;\n            }\n\n            /* some real error */\n\n            *err = \"PEM_read_bio_X509() failed\";\n            sk_X509_pop_free(chain, X509_free);\n            BIO_free(bio);\n            ERR_clear_error();\n            return NULL;\n        }\n\n        if (sk_X509_push(chain, x509) == 0) {\n            *err = \"sk_X509_push() failed\";\n            sk_X509_pop_free(chain, X509_free);\n            X509_free(x509);\n            BIO_free(bio);\n            ERR_clear_error();\n            return NULL;\n        }\n    }\n\n    BIO_free(bio);\n\n    return chain;\n}\n\n\nvoid *\nngx_http_lua_ffi_parse_der_cert(const char *data, size_t len,\n    char **err)\n{\n    BIO             *bio;\n    X509            *x509;\n    STACK_OF(X509)  *chain;\n\n    if (data == NULL || len == 0) {\n        *err = \"invalid argument\";\n        ERR_clear_error();\n        return NULL;\n    }\n\n    bio = BIO_new_mem_buf((char *) data, len);\n    if (bio == NULL) {\n        *err = \"BIO_new_mem_buf() failed\";\n        ERR_clear_error();\n        return NULL;\n    }\n\n    x509 = d2i_X509_bio(bio, NULL);\n    if (x509 == NULL) {\n        *err = \"d2i_X509_bio() failed\";\n        BIO_free(bio);\n        ERR_clear_error();\n        return NULL;\n    }\n\n    chain = sk_X509_new_null();\n    if (chain == NULL) {\n        *err = \"sk_X509_new_null() failed\";\n        X509_free(x509);\n        BIO_free(bio);\n        ERR_clear_error();\n        return NULL;\n    }\n\n    if (sk_X509_push(chain, x509) == 0) {\n        *err = \"sk_X509_push() failed\";\n        sk_X509_free(chain);\n        X509_free(x509);\n        BIO_free(bio);\n        ERR_clear_error();\n        return NULL;\n    }\n\n    /* read rest of the chain */\n\n    while (!BIO_eof(bio)) {\n        x509 = d2i_X509_bio(bio, NULL);\n        if (x509 == NULL) {\n            *err = \"d2i_X509_bio() failed in rest of chain\";\n            sk_X509_pop_free(chain, X509_free);\n            BIO_free(bio);\n            ERR_clear_error();\n            return NULL;\n        }\n\n        if (sk_X509_push(chain, x509) == 0) {\n            *err = \"sk_X509_push() failed in rest of chain\";\n            sk_X509_pop_free(chain, X509_free);\n            X509_free(x509);\n            BIO_free(bio);\n            ERR_clear_error();\n            return NULL;\n        }\n    }\n\n    BIO_free(bio);\n\n    return chain;\n}\n\n\nvoid\nngx_http_lua_ffi_free_cert(void *cdata)\n{\n    STACK_OF(X509)  *chain = cdata;\n\n    sk_X509_pop_free(chain, X509_free);\n}\n\n\nvoid *\nngx_http_lua_ffi_parse_pem_priv_key(const u_char *pem, size_t pem_len,\n    char **err)\n{\n    BIO         *in;\n    EVP_PKEY    *pkey;\n\n    in = BIO_new_mem_buf((char *) pem, (int) pem_len);\n    if (in == NULL) {\n        *err = \"BIO_new_mem_buf() failed\";\n        ERR_clear_error();\n        return NULL;\n    }\n\n    pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);\n    if (pkey == NULL) {\n        *err = \"PEM_read_bio_PrivateKey() failed\";\n        BIO_free(in);\n        ERR_clear_error();\n        return NULL;\n    }\n\n    BIO_free(in);\n\n    return pkey;\n}\n\n\nvoid *\nngx_http_lua_ffi_parse_der_priv_key(const char *data, size_t len,\n    char **err)\n{\n    BIO               *bio = NULL;\n    EVP_PKEY          *pkey = NULL;\n\n    if (data == NULL || len == 0) {\n        *err = \"invalid argument\";\n        ERR_clear_error();\n        return NULL;\n    }\n\n    bio = BIO_new_mem_buf((char *) data, len);\n    if (bio == NULL) {\n        *err = \"BIO_new_mem_buf() failed\";\n        ERR_clear_error();\n        return NULL;\n    }\n\n    pkey = d2i_PrivateKey_bio(bio, NULL);\n    if (pkey == NULL) {\n        *err = \"d2i_PrivateKey_bio() failed\";\n        BIO_free(bio);\n        ERR_clear_error();\n        return NULL;\n    }\n\n    BIO_free(bio);\n\n    return pkey;\n}\n\n\nvoid\nngx_http_lua_ffi_free_priv_key(void *cdata)\n{\n    EVP_PKEY *pkey = cdata;\n\n    EVP_PKEY_free(pkey);\n}\n\n\nint\nngx_http_lua_ffi_set_cert(ngx_http_request_t *r,\n    void *cdata, char **err)\n{\n#ifdef LIBRESSL_VERSION_NUMBER\n\n    *err = \"LibreSSL not supported\";\n    return NGX_ERROR;\n\n#else\n\n#   if OPENSSL_VERSION_NUMBER < 0x1000205fL\n\n    *err = \"at least OpenSSL 1.0.2e required but found \" OPENSSL_VERSION_TEXT;\n    return NGX_ERROR;\n\n#   else\n\n#ifdef OPENSSL_IS_BORINGSSL\n    size_t             i;\n#else\n    int                i;\n#endif\n    X509              *x509 = NULL;\n    ngx_ssl_conn_t    *ssl_conn;\n    STACK_OF(X509)    *chain = cdata;\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    if (sk_X509_num(chain) < 1) {\n        *err = \"invalid certificate chain\";\n        goto failed;\n    }\n\n    x509 = sk_X509_value(chain, 0);\n    if (x509 == NULL) {\n        *err = \"sk_X509_value() failed\";\n        goto failed;\n    }\n\n    if (SSL_use_certificate(ssl_conn, x509) == 0) {\n        *err = \"SSL_use_certificate() failed\";\n        goto failed;\n    }\n\n    x509 = NULL;\n\n    /* read rest of the chain */\n\n    for (i = 1; i < sk_X509_num(chain); i++) {\n\n        x509 = sk_X509_value(chain, i);\n        if (x509 == NULL) {\n            *err = \"sk_X509_value() failed\";\n            goto failed;\n        }\n\n        if (SSL_add1_chain_cert(ssl_conn, x509) == 0) {\n            *err = \"SSL_add1_chain_cert() failed\";\n            goto failed;\n        }\n    }\n\n    *err = NULL;\n    return NGX_OK;\n\nfailed:\n\n    ERR_clear_error();\n\n    return NGX_ERROR;\n\n#   endif  /* OPENSSL_VERSION_NUMBER < 0x1000205fL */\n#endif\n}\n\n\nint\nngx_http_lua_ffi_set_priv_key(ngx_http_request_t *r,\n    void *cdata, char **err)\n{\n    EVP_PKEY          *pkey = NULL;\n    ngx_ssl_conn_t    *ssl_conn;\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    pkey = cdata;\n    if (pkey == NULL) {\n        *err = \"invalid private key\";\n        goto failed;\n    }\n\n    if (SSL_use_PrivateKey(ssl_conn, pkey) == 0) {\n        *err = \"SSL_use_PrivateKey() failed\";\n        goto failed;\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    ERR_clear_error();\n\n    return NGX_ERROR;\n}\n\n\n#ifndef LIBRESSL_VERSION_NUMBER\nstatic int\nngx_http_lua_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)\n{\n    /*\n     * we never terminate handshake here and user can later use\n     * $ssl_client_verify to check verification result.\n     *\n     * this is consistent with Nginx behavior.\n     */\n    return 1;\n}\n#endif\n\n\nint\nngx_http_lua_ffi_ssl_verify_client(ngx_http_request_t *r, void *client_certs,\n    void *trusted_certs, int depth, char **err)\n{\n#ifdef LIBRESSL_VERSION_NUMBER\n\n    *err = \"LibreSSL not supported\";\n    return NGX_ERROR;\n\n#else\n\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_ssl_conn_t              *ssl_conn;\n    ngx_http_ssl_srv_conf_t     *sscf;\n    STACK_OF(X509)              *client_chain = client_certs;\n    STACK_OF(X509)              *trusted_chain = trusted_certs;\n    STACK_OF(X509_NAME)         *name_chain = NULL;\n    X509                        *x509 = NULL;\n    X509_NAME                   *subject = NULL;\n    X509_STORE                  *ca_store = NULL;\n#ifdef OPENSSL_IS_BORINGSSL\n    size_t                      i;\n#else\n    int                         i;\n#endif\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        *err = \"no request ctx found\";\n        return NGX_ERROR;\n    }\n\n    if (!(ctx->context & NGX_HTTP_LUA_CONTEXT_SSL_CERT)) {\n        *err = \"API disabled in the current context\";\n        return NGX_ERROR;\n    }\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    /* enable verify */\n\n    SSL_set_verify(ssl_conn, SSL_VERIFY_PEER, ngx_http_lua_ssl_verify_callback);\n\n    /* set depth */\n\n    if (depth < 0) {\n        sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);\n        if (sscf != NULL) {\n            depth = sscf->verify_depth;\n\n        } else {\n            /* same as the default value of ssl_verify_depth */\n            depth = 1;\n        }\n    }\n\n    SSL_set_verify_depth(ssl_conn, depth);\n\n    /* set CA chain */\n\n    if (client_chain != NULL || trusted_chain != NULL) {\n\n        ca_store = X509_STORE_new();\n        if (ca_store == NULL) {\n            *err = \"X509_STORE_new() failed\";\n            return NGX_ERROR;\n        }\n\n        if (client_chain != NULL) {\n\n            /* construct name chain */\n            name_chain = sk_X509_NAME_new_null();\n            if (name_chain == NULL) {\n                *err = \"sk_X509_NAME_new_null() failed\";\n                goto failed;\n            }\n\n            for (i = 0; i < sk_X509_num(client_chain); i++) {\n                x509 = sk_X509_value(client_chain, i);\n                if (x509 == NULL) {\n                    *err = \"sk_X509_value() failed\";\n                    goto failed;\n                }\n\n                /* add subject to name chain, which will be sent to client */\n                subject = X509_NAME_dup(X509_get_subject_name(x509));\n                if (subject == NULL) {\n                    *err = \"X509_get_subject_name() failed\";\n                    goto failed;\n                }\n\n                if (!sk_X509_NAME_push(name_chain, subject)) {\n                    *err = \"sk_X509_NAME_push() failed\";\n                    X509_NAME_free(subject);\n                    goto failed;\n                }\n\n                /* add to trusted CA store */\n                if (X509_STORE_add_cert(ca_store, x509) == 0) {\n                    *err = \"X509_STORE_add_cert() failed\";\n                    goto failed;\n                }\n            }\n\n            /* clean subject name list, and set it for send to client */\n            SSL_set_client_CA_list(ssl_conn, name_chain);\n        }\n\n        if (trusted_chain != NULL) {\n            for (i = 0; i < sk_X509_num(trusted_chain); i++) {\n                x509 = sk_X509_value(trusted_chain, i);\n                if (x509 == NULL) {\n                    *err = \"sk_X509_value() failed\";\n                    goto failed;\n                }\n\n                /* add to trusted CA store */\n                if (X509_STORE_add_cert(ca_store, x509) == 0) {\n                    *err = \"X509_STORE_add_cert() failed\";\n                    goto failed;\n                }\n            }\n        }\n\n        /* clean ca_store, and store new ca_store */\n        if (SSL_set0_verify_cert_store(ssl_conn, ca_store) == 0) {\n            *err = \"SSL_set0_verify_cert_store() failed\";\n            goto failed;\n        }\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    sk_X509_NAME_free(name_chain);\n\n    X509_STORE_free(ca_store);\n\n    return NGX_ERROR;\n#endif\n}\n\n\nngx_ssl_conn_t *\nngx_http_lua_ffi_get_req_ssl_pointer(ngx_http_request_t *r, const char **err)\n{\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NULL;\n    }\n\n    if (r->connection->ssl->connection == NULL) {\n        *err = \"bad ssl connection\";\n        return NULL;\n    }\n\n    return r->connection->ssl->connection;\n}\n\n\nint\nngx_http_lua_ffi_ssl_client_random(ngx_http_request_t *r,\n    unsigned char *out, size_t *outlen, char **err)\n{\n    ngx_ssl_conn_t          *ssl_conn;\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    *outlen = SSL_get_client_random(ssl_conn, out, *outlen);\n\n    return NGX_OK;\n}\n\n\n#endif /* NGX_HTTP_SSL */\n"
  },
  {
    "path": "src/ngx_http_lua_ssl_certby.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_SSL_CERTBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_SSL_CERTBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\n#if (NGX_HTTP_SSL)\n\n\nngx_int_t ngx_http_lua_ssl_cert_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L);\n\nngx_int_t ngx_http_lua_ssl_cert_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L);\n\nchar *ngx_http_lua_ssl_cert_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nchar *ngx_http_lua_ssl_cert_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nint ngx_http_lua_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data);\n\n\n#endif  /* NGX_HTTP_SSL */\n\n\n#endif /* _NGX_HTTP_LUA_SSL_CERTBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_ssl_client_helloby.c",
    "content": "/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#if (NGX_HTTP_SSL)\n\n\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_initworkerby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_ssl_module.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_ssl_client_helloby.h\"\n#include \"ngx_http_lua_directive.h\"\n#include \"ngx_http_lua_ssl.h\"\n\n\nstatic void ngx_http_lua_ssl_client_hello_done(void *data);\nstatic void ngx_http_lua_ssl_client_hello_aborted(void *data);\nstatic u_char *ngx_http_lua_log_ssl_client_hello_error(ngx_log_t *log,\n    u_char *buf, size_t len);\nstatic ngx_int_t ngx_http_lua_ssl_client_hello_by_chunk(lua_State *L,\n    ngx_http_request_t *r);\n\n\nngx_int_t\nngx_http_lua_ssl_client_hello_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L,\n                                     lscf->srv.ssl_client_hello_src.data,\n                                     &lscf->srv.ssl_client_hello_src_ref,\n                                     lscf->srv.ssl_client_hello_src_key);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_ssl_client_hello_by_chunk(L, r);\n}\n\n\nngx_int_t\nngx_http_lua_ssl_client_hello_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       lscf->srv.ssl_client_hello_src.data,\n                                       lscf->srv.ssl_client_hello_src.len,\n                                       &lscf->srv.ssl_client_hello_src_ref,\n                                       lscf->srv.ssl_client_hello_src_key,\n                           (const char *) lscf->srv.ssl_client_hello_chunkname);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_ssl_client_hello_by_chunk(L, r);\n}\n\n\nchar *\nngx_http_lua_ssl_client_hello_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_ssl_client_hello_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nchar *\nngx_http_lua_ssl_client_hello_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n#ifndef SSL_ERROR_WANT_CLIENT_HELLO_CB\n\n    ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                  \"at least OpenSSL 1.1.1 required but found \"\n                  OPENSSL_VERSION_TEXT);\n\n    return NGX_CONF_ERROR;\n\n#else\n\n    size_t                       chunkname_len;\n    u_char                      *chunkname;\n    u_char                      *cache_key = NULL;\n    u_char                      *name;\n    ngx_str_t                   *value;\n    ngx_http_lua_srv_conf_t     *lscf = conf;\n\n    /*  must specify a concrete handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (lscf->srv.ssl_client_hello_handler) {\n        return \"is duplicate\";\n    }\n\n    if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    lscf->srv.ssl_client_hello_handler =\n        (ngx_http_lua_srv_conf_handler_pt) cmd->post;\n\n    if (cmd->post == ngx_http_lua_ssl_client_hello_handler_file) {\n        /* Lua code in an external file */\n\n        name = ngx_http_lua_rebase_path(cf->pool, value[1].data,\n                                        value[1].len);\n        if (name == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                    value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        lscf->srv.ssl_client_hello_src.data = name;\n        lscf->srv.ssl_client_hello_src.len = ngx_strlen(name);\n\n    } else {\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf,\n                                                     \"ssl_client_hello_by_lua\",\n                                                     value[1].data,\n                                                     value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"ssl_client_hello_by_lua\",\n                                          sizeof(\"ssl_client_hello_by_lua\") - 1,\n                                          &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        lscf->srv.ssl_client_hello_src = value[1];\n        lscf->srv.ssl_client_hello_chunkname = chunkname;\n    }\n\n    lscf->srv.ssl_client_hello_src_key = cache_key;\n\n    return NGX_CONF_OK;\n\n#endif  /* NO SSL_ERROR_WANT_CLIENT_HELLO_CB */\n}\n\n\nint\nngx_http_lua_ssl_client_hello_handler(ngx_ssl_conn_t *ssl_conn,\n    int *al, void *arg)\n{\n    lua_State                       *L;\n    ngx_int_t                        rc;\n    ngx_connection_t                *c, *fc;\n    ngx_http_request_t              *r = NULL;\n    ngx_pool_cleanup_t              *cln;\n    ngx_http_connection_t           *hc;\n    ngx_http_lua_srv_conf_t         *lscf;\n    ngx_http_core_loc_conf_t        *clcf;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n    ngx_http_core_srv_conf_t        *cscf;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"ssl client hello: connection reusable: %ud\", c->reusable);\n\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n\n    dd(\"ssl client hello handler, client-hello-ctx=%p\", cctx);\n\n    if (cctx && cctx->entered_client_hello_handler) {\n        /* not the first time */\n\n        if (cctx->done) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"ssl_client_hello_by_lua: \"\n                           \"client hello cb exit code: %d\",\n                           cctx->exit_code);\n\n            dd(\"lua ssl client hello done, finally\");\n            return cctx->exit_code;\n        }\n\n        return -1;\n    }\n\n    dd(\"first time\");\n\n#if (nginx_version > 1029001)\n#ifdef SSL_CLIENT_HELLO_SUCCESS\n#if !defined freenginx\n    /* see commit 0373fe5d98c1515640 for more details */\n    rc = ngx_ssl_client_hello_callback(ssl_conn, al, arg);\n\n    if (rc == 0) {\n        return rc;\n    }\n#endif\n#endif\n#endif\n\n#if (nginx_version < 1017009)\n    ngx_reusable_connection(c, 0);\n#endif\n\n    hc = c->data;\n\n    fc = ngx_http_lua_create_fake_connection(NULL);\n    if (fc == NULL) {\n        goto failed;\n    }\n\n    fc->log->handler = ngx_http_lua_log_ssl_client_hello_error;\n    fc->log->data = fc;\n\n    fc->addr_text = c->addr_text;\n    fc->listening = c->listening;\n\n    r = ngx_http_lua_create_fake_request(fc);\n    if (r == NULL) {\n        goto failed;\n    }\n\n    r->main_conf = hc->conf_ctx->main_conf;\n    r->srv_conf = hc->conf_ctx->srv_conf;\n    r->loc_conf = hc->conf_ctx->loc_conf;\n\n    fc->log->file = c->log->file;\n    fc->log->log_level = c->log->log_level;\n    fc->ssl = c->ssl;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n\n#if nginx_version >= 1009000\n    ngx_set_connection_log(fc, clcf->error_log);\n#else\n    ngx_http_set_connection_log(fc, clcf->error_log);\n#endif\n\n    if (cctx == NULL) {\n        cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t));\n        if (cctx == NULL) {\n            goto failed;  /* error */\n        }\n\n        cctx->ctx_ref = LUA_NOREF;\n    }\n\n    cctx->exit_code = 1;  /* successful by default */\n    cctx->connection = c;\n    cctx->request = r;\n    cctx->entered_client_hello_handler = 1;\n    cctx->done = 0;\n\n    dd(\"setting cctx\");\n\n    if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index, cctx)\n        == 0)\n    {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"SSL_set_ex_data() failed\");\n        goto failed;\n    }\n\n    lscf = ngx_http_get_module_srv_conf(r, ngx_http_lua_module);\n\n    /* TODO honor lua_code_cache off */\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    c->log->action = \"loading SSL client hello by lua\";\n\n    if (lscf->srv.ssl_client_hello_handler == NULL) {\n        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                      \"no ssl_client_hello_by_lua* defined in \"\n                      \"server %V\", &cscf->server_name);\n\n        goto failed;\n    }\n\n    rc = lscf->srv.ssl_client_hello_handler(r, lscf, L);\n\n    if (rc >= NGX_OK || rc == NGX_ERROR) {\n        cctx->done = 1;\n\n        if (cctx->cleanup) {\n            *cctx->cleanup = NULL;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"ssl_client_hello_by_lua: handler return value: %i, \"\n                       \"client hello cb exit code: %d\", rc, cctx->exit_code);\n\n        c->log->action = \"SSL handshaking\";\n        return cctx->exit_code;\n    }\n\n    /* rc == NGX_DONE */\n\n    cln = ngx_pool_cleanup_add(fc->pool, 0);\n    if (cln == NULL) {\n        goto failed;\n    }\n\n    cln->handler = ngx_http_lua_ssl_client_hello_done;\n    cln->data = cctx;\n\n    if (cctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(c->pool, 0);\n        if (cln == NULL) {\n            goto failed;\n        }\n\n        cln->data = cctx;\n        cctx->cleanup = &cln->handler;\n    }\n\n    *cctx->cleanup = ngx_http_lua_ssl_client_hello_aborted;\n\n    return -1;\n\nfailed:\n\n    if (r && r->pool) {\n        ngx_http_lua_free_fake_request(r);\n    }\n\n    if (fc) {\n        ngx_http_lua_close_fake_connection(fc);\n    }\n\n    return 0;\n}\n\n\nstatic void\nngx_http_lua_ssl_client_hello_done(void *data)\n{\n    ngx_connection_t        *c;\n    ngx_http_lua_ssl_ctx_t  *cctx = data;\n\n    dd(\"lua ssl client hello done\");\n\n    if (cctx->aborted) {\n        return;\n    }\n\n    ngx_http_lua_assert(cctx->done == 0);\n\n    cctx->done = 1;\n\n    if (cctx->cleanup) {\n        *cctx->cleanup = NULL;\n    }\n\n    c = cctx->connection;\n\n    c->log->action = \"SSL handshaking\";\n\n    ngx_post_event(c->write, &ngx_posted_events);\n\n#if (HAVE_QUIC_SSL_LUA_YIELD_PATCH && NGX_HTTP_V3)\n#   if defined(SSL_ERROR_WANT_CLIENT_HELLO_CB)\n#       if (NGX_QUIC_OPENSSL_COMPAT || NGX_QUIC_OPENSSL_API)\n    ngx_http_lua_resume_quic_ssl_handshake(c);\n#       endif\n#   endif\n#endif\n}\n\n\nstatic void\nngx_http_lua_ssl_client_hello_aborted(void *data)\n{\n    ngx_http_lua_ssl_ctx_t      *cctx = data;\n\n    dd(\"lua ssl client hello aborted\");\n\n    if (cctx->done) {\n        /* completed successfully already */\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0,\n                   \"ssl_client_hello_by_lua: client hello cb aborted\");\n\n    cctx->aborted = 1;\n    cctx->request->connection->ssl = NULL;\n\n    ngx_http_lua_finalize_fake_request(cctx->request, NGX_ERROR);\n}\n\n\nstatic u_char *\nngx_http_lua_log_ssl_client_hello_error(ngx_log_t *log,\n    u_char *buf, size_t len)\n{\n    u_char              *p;\n    ngx_connection_t    *c;\n\n    if (log->action) {\n        p = ngx_snprintf(buf, len, \" while %s\", log->action);\n        len -= p - buf;\n        buf = p;\n    }\n\n    p = ngx_snprintf(buf, len, \", context: ssl_client_hello_by_lua*\");\n    len -= p - buf;\n    buf = p;\n\n    c = log->data;\n\n    if (c && c->addr_text.len) {\n        p = ngx_snprintf(buf, len, \", client: %V\", &c->addr_text);\n        len -= p - buf;\n        buf = p;\n    }\n\n    if (c && c->listening && c->listening->addr_text.len) {\n        p = ngx_snprintf(buf, len, \", server: %V\", &c->listening->addr_text);\n        /* len -= p - buf; */\n        buf = p;\n    }\n\n    return buf;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_ssl_client_hello_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    int                      co_ref;\n    ngx_int_t                rc;\n    lua_State               *co;\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_pool_cleanup_t      *cln;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            rc = NGX_ERROR;\n            ngx_http_lua_finalize_request(r, rc);\n            return rc;\n        }\n\n    } else {\n        dd(\"reset ctx\");\n        ngx_http_lua_reset_ctx(r, L, ctx);\n    }\n\n    ctx->entered_content_phase = 1;\n\n    /*  {{{ new coroutine to handle request */\n    co = ngx_http_lua_new_thread(r, L, &co_ref);\n\n    if (co == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"lua: failed to create new coroutine to handle request\");\n\n        rc = NGX_ERROR;\n        ngx_http_lua_finalize_request(r, rc);\n        return rc;\n    }\n\n    /*  move code closure to new coroutine */\n    lua_xmove(L, co, 1);\n\n#ifndef OPENRESTY_LUAJIT\n    /*  set closure's env table to new coroutine's globals table */\n    ngx_http_lua_get_globals_table(co);\n    lua_setfenv(co, -2);\n#endif\n\n    /* save nginx request in coroutine globals table */\n    ngx_http_lua_set_req(co, r);\n\n    ctx->cur_co_ctx = &ctx->entry_co_ctx;\n    ctx->cur_co_ctx->co = co;\n    ctx->cur_co_ctx->co_ref = co_ref;\n#ifdef NGX_LUA_USE_ASSERT\n    ctx->cur_co_ctx->co_top = 1;\n#endif\n\n    ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);\n\n    /* register request cleanup hooks */\n    if (ctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n        if (cln == NULL) {\n            rc = NGX_ERROR;\n            ngx_http_lua_finalize_request(r, rc);\n            return rc;\n        }\n\n        cln->handler = ngx_http_lua_request_cleanup_handler;\n        cln->data = ctx;\n        ctx->cleanup = &cln->handler;\n    }\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO;\n\n    rc = ngx_http_lua_run_thread(L, r, ctx, 0);\n\n    if (rc == NGX_ERROR || rc >= NGX_OK) {\n        /* do nothing */\n\n    } else if (rc == NGX_AGAIN) {\n        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0);\n\n    } else if (rc == NGX_DONE) {\n        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 1);\n\n    } else {\n        rc = NGX_OK;\n    }\n\n    ngx_http_lua_finalize_request(r, rc);\n    return rc;\n}\n\n\nint\nngx_http_lua_ffi_ssl_get_client_hello_server_name(ngx_http_request_t *r,\n    const char **name, size_t *namelen, char **err)\n{\n#ifdef LIBRESSL_VERSION_NUMBER\n    *err = \"LibreSSL does not support by ssl_client_hello_by_lua*\";\n    return NGX_ERROR;\n#else\n    ngx_ssl_conn_t          *ssl_conn;\n#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB\n    const unsigned char     *p;\n    size_t                   remaining, len;\n#endif\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n\n#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB\n    remaining = 0;\n\n    /* This code block is taken from OpenSSL's client_hello_select_server_ctx()\n     * */\n    if (!SSL_client_hello_get0_ext(ssl_conn, TLSEXT_TYPE_server_name, &p,\n                                   &remaining))\n    {\n        return NGX_DECLINED;\n    }\n\n    if (remaining <= 2) {\n        *err = \"Bad SSL Client Hello Extension\";\n        return NGX_ERROR;\n    }\n\n    len = (*(p++) << 8);\n    len += *(p++);\n    if (len + 2 != remaining) {\n        *err = \"Bad SSL Client Hello Extension\";\n        return NGX_ERROR;\n    }\n\n    remaining = len;\n    if (remaining == 0 || *p++ != TLSEXT_NAMETYPE_host_name) {\n        *err = \"Bad SSL Client Hello Extension\";\n        return NGX_ERROR;\n    }\n\n    remaining--;\n    if (remaining <= 2) {\n        *err = \"Bad SSL Client Hello Extension\";\n        return NGX_ERROR;\n    }\n\n    len = (*(p++) << 8);\n    len += *(p++);\n    if (len + 2 > remaining) {\n        *err = \"Bad SSL Client Hello Extension\";\n        return NGX_ERROR;\n    }\n\n    remaining = len;\n    *name = (const char *) p;\n    *namelen = len;\n\n    return NGX_OK;\n\n#else\n    *err = \"OpenSSL too old to support this function\";\n    return NGX_ERROR;\n\n#endif\n\n#else\n\n    *err = \"no TLS extension support\";\n    return NGX_ERROR;\n#endif\n#endif  /* LIBRESSL_VERSION_NUMBER */\n}\n\n\nint\nngx_http_lua_ffi_ssl_get_client_hello_ext(ngx_http_request_t *r,\n    unsigned int type, const unsigned char **out, size_t *outlen, char **err)\n{\n#ifdef LIBRESSL_VERSION_NUMBER\n    *err = \"LibreSSL does not support by ssl_client_hello_by_lua*\";\n    return NGX_ERROR;\n#else\n    ngx_ssl_conn_t          *ssl_conn;\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB\n    if (SSL_client_hello_get0_ext(ssl_conn, type, out, outlen) == 0) {\n        return NGX_DECLINED;\n    }\n\n    return NGX_OK;\n#else\n    *err = \"OpenSSL too old to support this function\";\n    return NGX_ERROR;\n#endif\n#endif  /* LIBRESSL_VERSION_NUMBER */\n}\n\n\nint\nngx_http_lua_ffi_ssl_get_client_hello_ext_present(ngx_http_request_t *r,\n    int **extensions, size_t *extensions_len, char **err)\n{\n#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB\n    ngx_ssl_conn_t          *ssl_conn;\n    int                      got_extensions;\n    size_t                   ext_len;\n    int                     *ext_out;\n    /* OPENSSL will allocate memory for us and make the ext_out point to it */\n\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    got_extensions = SSL_client_hello_get1_extensions_present(ssl_conn,\n                                                           &ext_out, &ext_len);\n    if (!got_extensions || !ext_out || !ext_len) {\n        *err = \"failed SSL_client_hello_get1_extensions_present()\";\n        return NGX_DECLINED;\n    }\n\n    *extensions = ngx_palloc(r->connection->pool, sizeof(int) * ext_len);\n    if (*extensions != NULL) {\n        ngx_memcpy(*extensions, ext_out, sizeof(int) * ext_len);\n        *extensions_len = ext_len;\n    }\n\n    OPENSSL_free(ext_out);\n    return NGX_OK;\n#else\n    *err = \"OpenSSL too old to support this function\";\n    return NGX_ERROR;\n#endif\n}\n\n\nint ngx_http_lua_ffi_ssl_get_client_hello_ciphers(ngx_http_request_t *r,\n    unsigned short *ciphers,  size_t ciphers_size, char **err)\n{\n#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB\n    int                      i;\n    size_t                   ciphers_cnt;\n    size_t                   ciphersuites_bytes;\n    ngx_ssl_conn_t          *ssl_conn;\n    const unsigned char     *ciphers_raw;\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    ciphersuites_bytes = SSL_client_hello_get0_ciphers(ssl_conn, &ciphers_raw);\n\n    if (ciphersuites_bytes == 0) {\n        *err = \"failed SSL_client_hello_get0_ciphers()\";\n        return NGX_DECLINED;\n    }\n\n    if (ciphersuites_bytes % 2 != 0) {\n        *err = \"SSL_client_hello_get0_ciphers() odd ciphersuites_bytes\";\n        return NGX_DECLINED;\n    }\n\n    ciphers_cnt = ciphersuites_bytes / 2;\n    ciphers_cnt = ciphers_cnt > ciphers_size ? ciphers_size : ciphers_cnt;\n\n    for (i = 0 ; i < (int) ciphers_cnt ; i++) {\n        ciphers[i] = (ciphers_raw[i * 2] << 8) | ciphers_raw[i * 2 + 1];\n    }\n\n    return ciphers_cnt;\n#else\n    *err = \"OpenSSL too old to support this function\";\n    return NGX_ERROR;\n#endif\n}\n\n\nint\nngx_http_lua_ffi_ssl_set_protocols(ngx_http_request_t *r,\n    int protocols, char **err)\n{\n\n    ngx_ssl_conn_t    *ssl_conn;\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n#if OPENSSL_VERSION_NUMBER >= 0x009080dfL\n    /* only in 0.9.8m+ */\n    SSL_clear_options(ssl_conn,\n                      SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1);\n#endif\n\n    if (!(protocols & NGX_SSL_SSLv2)) {\n        SSL_set_options(ssl_conn, SSL_OP_NO_SSLv2);\n    }\n\n    if (!(protocols & NGX_SSL_SSLv3)) {\n        SSL_set_options(ssl_conn, SSL_OP_NO_SSLv3);\n    }\n\n    if (!(protocols & NGX_SSL_TLSv1)) {\n        SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1);\n    }\n\n#ifdef SSL_OP_NO_TLSv1_1\n    SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_1);\n    if (!(protocols & NGX_SSL_TLSv1_1)) {\n        SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_1);\n    }\n#endif\n\n#ifdef SSL_OP_NO_TLSv1_2\n    SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_2);\n    if (!(protocols & NGX_SSL_TLSv1_2)) {\n        SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_2);\n    }\n#endif\n\n#if defined(NGX_SSL_TLSv1_3) && defined( SSL_OP_NO_TLSv1_3)\n    SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_3);\n    if (!(protocols & NGX_SSL_TLSv1_3)) {\n        SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_3);\n    }\n#endif\n\n    return NGX_OK;\n}\n\n#endif /* NGX_HTTP_SSL */\n"
  },
  {
    "path": "src/ngx_http_lua_ssl_client_helloby.h",
    "content": "/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n#ifndef _NGX_HTTP_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\n#if (NGX_HTTP_SSL)\n\nngx_int_t ngx_http_lua_ssl_client_hello_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L);\n\nngx_int_t ngx_http_lua_ssl_client_hello_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L);\n\nchar *ngx_http_lua_ssl_client_hello_by_lua_block(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\n\nchar *ngx_http_lua_ssl_client_hello_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nint ngx_http_lua_ssl_client_hello_handler(ngx_ssl_conn_t *ssl_conn,\n    int *al, void *arg);\n\n\n#endif  /* NGX_HTTP_SSL */\n\n\n#endif /* _NGX_HTTP_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_ssl_export_keying_material.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n\n\n#include \"ddebug.h\"\n\n#if (NGX_HTTP_SSL)\n\n#include <openssl/ssl.h>\n\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_initworkerby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_ssl_module.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_ssl_session_fetchby.h\"\n#include \"ngx_http_lua_ssl.h\"\n#include \"ngx_http_lua_directive.h\"\n#include \"ngx_http_lua_ssl_export_keying_material.h\"\n\n\nngx_int_t\nngx_http_lua_ffi_ssl_export_keying_material(ngx_http_request_t *r,\n    u_char *out, size_t out_size, const char *label, size_t llen,\n    const u_char *context, size_t ctxlen, int use_ctx, char **err)\n{\n#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER < 0x10101000L\n    *err = \"BoringSSL does not support SSL_export_keying_material\";\n    return NGX_ERROR;\n#elif defined(LIBRESSL_VERSION_NUMBER)\n    *err = \"LibreSSL does not support SSL_export_keying_material\";\n    return NGX_ERROR;\n#elif OPENSSL_VERSION_NUMBER < 0x10101000L\n    *err = \"OpenSSL too old\";\n    return NGX_ERROR;\n#else\n    ngx_connection_t   *c;\n    ngx_ssl_conn_t     *ssl_conn;\n    int                 rc;\n\n    c = r->connection;\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl connection\";\n        return NGX_ERROR;\n    }\n\n    rc = SSL_export_keying_material(ssl_conn, out, out_size, label, llen,\n                                    context, ctxlen, use_ctx);\n    if (rc == 1) {\n        return NGX_OK;\n    }\n\n    ngx_ssl_error(NGX_LOG_INFO, c->log, 0,\n                  \"SSL_export_keying_material rc: %d, error: %s\",\n                  rc, ERR_error_string(ERR_get_error(), NULL));\n\n    *err = \"SSL_export_keying_material() failed\";\n\n    return NGX_ERROR;\n#endif\n}\n\n\nngx_int_t\nngx_http_lua_ffi_ssl_export_keying_material_early(ngx_http_request_t *r,\n    u_char *out, size_t out_size, const char *label, size_t llen,\n    const u_char *context, size_t ctxlen, char **err)\n{\n#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER < 0x10101000L\n    *err = \"BoringSSL does not support SSL_export_keying_material\";\n    return NGX_ERROR;\n#elif defined(LIBRESSL_VERSION_NUMBER)\n    *err = \"LibreSSL does not support SSL_export_keying_material\";\n    return NGX_ERROR;\n#elif OPENSSL_VERSION_NUMBER < 0x10101000L\n    *err = \"OpenSSL too old\";\n    return NGX_ERROR;\n#else\n    int                  rc;\n    ngx_ssl_conn_t      *ssl_conn;\n    ngx_connection_t    *c;\n\n    c = r->connection;\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl connection\";\n        return NGX_ERROR;\n    }\n\n    rc = SSL_export_keying_material_early(ssl_conn, out, out_size,\n                                          label, llen, context, ctxlen);\n\n    if (rc == 1) {\n        return NGX_OK;\n    }\n\n    ngx_ssl_error(NGX_LOG_INFO, c->log, 0,\n                  \"SSL_export_keying_material_early rc: %d, error: %s\",\n                  rc, ERR_error_string(ERR_get_error(), NULL));\n\n    *err = \"SSL_export_keying_material_early() failed\";\n\n    return NGX_ERROR;\n#endif\n}\n\n#endif /* NGX_HTTP_SSL */\n"
  },
  {
    "path": "src/ngx_http_lua_ssl_export_keying_material.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_SSL_EXPORT_KEYING_MATERIAL_H_INCLUDED_\n#define _NGX_HTTP_LUA_SSL_EXPORT_KEYING_MATERIAL_H_INCLUDED_\n\n#include \"ngx_http_lua_common.h\"\n\n#if (NGX_HTTP_SSL)\nngx_int_t ngx_http_lua_ffi_ssl_export_keying_material(ngx_http_request_t *r,\n    u_char *out, size_t out_size, const char *label, size_t llen,\n    const u_char *ctx, size_t ctxlen, int use_ctx, char **err);\n\nngx_int_t ngx_http_lua_ffi_ssl_export_keying_material_early(\n    ngx_http_request_t *r, u_char *out, size_t out_size, const char *label,\n    size_t llen, const u_char *ctx, size_t ctxlen, char **err);\n#endif\n\n#endif /* _NGX_HTTP_LUA_SSL_EXPORT_KEYING_MATERIAL_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_ssl_ocsp.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#if (NGX_HTTP_SSL)\n\n\n#include \"ngx_http_lua_common.h\"\n\n\n#ifdef NGX_HTTP_LUA_USE_OCSP\nstatic int ngx_http_lua_ssl_empty_status_callback(ngx_ssl_conn_t *ssl_conn,\n    void *data);\n\nstatic long ngx_http_lua_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time);\n#endif\n\nint\nngx_http_lua_ffi_ssl_get_ocsp_responder_from_der_chain(\n    const char *chain_data, size_t chain_len, unsigned char *out,\n    size_t *out_size, char **err)\n{\n#ifndef NGX_HTTP_LUA_USE_OCSP\n\n    *err = \"no OCSP support\";\n    return NGX_ERROR;\n\n#else\n\n    int                        rc = NGX_OK;\n    BIO                       *bio = NULL;\n    char                      *s;\n    X509                      *cert = NULL, *issuer = NULL;\n    size_t                     len;\n    STACK_OF(OPENSSL_STRING)  *aia = NULL;\n\n    /* certificate */\n\n    bio = BIO_new_mem_buf((char *) chain_data, chain_len);\n    if (bio == NULL) {\n        *err = \"BIO_new_mem_buf() failed\";\n        rc = NGX_ERROR;\n        goto done;\n    }\n\n    cert = d2i_X509_bio(bio, NULL);\n    if (cert == NULL) {\n        *err = \"d2i_X509_bio() failed\";\n        rc = NGX_ERROR;\n        goto done;\n    }\n\n    /* responder */\n\n    aia = X509_get1_ocsp(cert);\n    if (aia == NULL) {\n        rc = NGX_DECLINED;\n        goto done;\n    }\n\n#if OPENSSL_VERSION_NUMBER >= 0x10000000L\n    s = sk_OPENSSL_STRING_value(aia, 0);\n#else\n    s = sk_value(aia, 0);\n#endif\n    if (s == NULL) {\n        rc = NGX_DECLINED;\n        goto done;\n    }\n\n    len = ngx_strlen(s);\n    if (len > *out_size) {\n        len = *out_size;\n        rc = NGX_BUSY;\n\n    } else {\n        rc = NGX_OK;\n        *out_size = len;\n    }\n\n    ngx_memcpy(out, s, len);\n\n    X509_email_free(aia);\n    aia = NULL;\n\n    /* issuer */\n\n    if (BIO_eof(bio)) {\n        *err = \"no issuer certificate in chain\";\n        rc = NGX_ERROR;\n        goto done;\n    }\n\n    issuer = d2i_X509_bio(bio, NULL);\n    if (issuer == NULL) {\n        *err = \"d2i_X509_bio() failed\";\n        rc = NGX_ERROR;\n        goto done;\n    }\n\n    if (X509_check_issued(issuer, cert) != X509_V_OK) {\n        *err = \"issuer certificate not next to leaf\";\n        rc = NGX_ERROR;\n        goto done;\n    }\n\n    X509_free(issuer);\n    X509_free(cert);\n    BIO_free(bio);\n\n    return rc;\n\ndone:\n\n    if (aia) {\n        X509_email_free(aia);\n    }\n\n    if (issuer) {\n        X509_free(issuer);\n    }\n\n    if (cert) {\n        X509_free(cert);\n    }\n\n    if (bio) {\n        BIO_free(bio);\n    }\n\n    if (rc == NGX_ERROR) {\n        ERR_clear_error();\n    }\n\n    return rc;\n\n#endif  /* NGX_HTTP_LUA_USE_OCSP */\n}\n\n\nint\nngx_http_lua_ffi_ssl_create_ocsp_request(const char *chain_data,\n    size_t chain_len, unsigned char *out, size_t *out_size, char **err)\n{\n#ifndef NGX_HTTP_LUA_USE_OCSP\n\n    *err = \"no OCSP support\";\n    return NGX_ERROR;\n\n#else\n\n    int                        rc = NGX_ERROR;\n    BIO                       *bio = NULL;\n    X509                      *cert = NULL, *issuer = NULL;\n    size_t                     len;\n    OCSP_CERTID               *id;\n    OCSP_REQUEST              *ocsp = NULL;\n\n    /* certificate */\n\n    bio = BIO_new_mem_buf((char *) chain_data, chain_len);\n    if (bio == NULL) {\n        *err = \"BIO_new_mem_buf() failed\";\n        goto failed;\n    }\n\n    cert = d2i_X509_bio(bio, NULL);\n    if (cert == NULL) {\n        *err = \"d2i_X509_bio() failed\";\n        goto failed;\n    }\n\n    if (BIO_eof(bio)) {\n        *err = \"no issuer certificate in chain\";\n        goto failed;\n    }\n\n    issuer = d2i_X509_bio(bio, NULL);\n    if (issuer == NULL) {\n        *err = \"d2i_X509_bio() failed\";\n        goto failed;\n    }\n\n    ocsp = OCSP_REQUEST_new();\n    if (ocsp == NULL) {\n        *err = \"OCSP_REQUEST_new() failed\";\n        goto failed;\n    }\n\n    id = OCSP_cert_to_id(NULL, cert, issuer);\n    if (id == NULL) {\n        *err = \"OCSP_cert_to_id() failed\";\n        goto failed;\n    }\n\n    if (OCSP_request_add0_id(ocsp, id) == NULL) {\n        *err = \"OCSP_request_add0_id() failed\";\n        goto failed;\n    }\n\n    len = i2d_OCSP_REQUEST(ocsp, NULL);\n    if (len <= 0) {\n        *err = \"i2d_OCSP_REQUEST() failed\";\n        goto failed;\n    }\n\n    if (len > *out_size) {\n        *err = \"output buffer too small\";\n        *out_size = len;\n        rc = NGX_BUSY;\n        goto failed;\n    }\n\n    len = i2d_OCSP_REQUEST(ocsp, &out);\n    if (len <=  0) {\n        *err = \"i2d_OCSP_REQUEST() failed\";\n        goto failed;\n    }\n\n    *out_size = len;\n\n    OCSP_REQUEST_free(ocsp);\n    X509_free(issuer);\n    X509_free(cert);\n    BIO_free(bio);\n\n    return NGX_OK;\n\nfailed:\n\n    if (ocsp) {\n        OCSP_REQUEST_free(ocsp);\n    }\n\n    if (issuer) {\n        X509_free(issuer);\n    }\n\n    if (cert) {\n        X509_free(cert);\n    }\n\n    if (bio) {\n        BIO_free(bio);\n    }\n\n    ERR_clear_error();\n\n    return rc;\n\n#endif  /* NGX_HTTP_LUA_USE_OCSP */\n}\n\n\nint\nngx_http_lua_ffi_ssl_validate_ocsp_response(const u_char *resp,\n    size_t resp_len, const char *chain_data, size_t chain_len,\n    u_char *errbuf, size_t *errbuf_size, long *valid)\n{\n#ifndef NGX_HTTP_LUA_USE_OCSP\n\n    *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                \"no OCSP support\") - errbuf;\n    return NGX_ERROR;\n\n#else\n\n    int                    n;\n    BIO                   *bio = NULL;\n    X509                  *cert = NULL, *issuer = NULL;\n    OCSP_CERTID           *id = NULL;\n    OCSP_RESPONSE         *ocsp = NULL;\n    OCSP_BASICRESP        *basic = NULL;\n    STACK_OF(X509)        *chain = NULL;\n    ASN1_GENERALIZEDTIME  *thisupdate = NULL, *nextupdate = NULL;\n\n    ocsp = d2i_OCSP_RESPONSE(NULL, &resp, resp_len);\n    if (ocsp == NULL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"d2i_OCSP_RESPONSE() failed\") - errbuf;\n        goto error;\n    }\n\n    n = OCSP_response_status(ocsp);\n\n    if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"OCSP response not successful (%d: %s)\",\n                                    n, OCSP_response_status_str(n)) - errbuf;\n        goto error;\n    }\n\n    basic = OCSP_response_get1_basic(ocsp);\n    if (basic == NULL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"OCSP_response_get1_basic() failed\")\n                       - errbuf;\n        goto error;\n    }\n\n    /* get issuer certificate from chain */\n\n    bio = BIO_new_mem_buf((char *) chain_data, chain_len);\n    if (bio == NULL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"BIO_new_mem_buf() failed\")\n                       - errbuf;\n        goto error;\n    }\n\n    cert = d2i_X509_bio(bio, NULL);\n    if (cert == NULL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"d2i_X509_bio() failed\")\n                       - errbuf;\n        goto error;\n    }\n\n    if (BIO_eof(bio)) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"no issuer certificate in chain\")\n                       - errbuf;\n        goto error;\n    }\n\n    issuer = d2i_X509_bio(bio, NULL);\n    if (issuer == NULL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"d2i_X509_bio() failed\") - errbuf;\n        goto error;\n    }\n\n    chain = sk_X509_new_null();\n    if (chain == NULL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"sk_X509_new_null() failed\") - errbuf;\n        goto error;\n    }\n\n    (void) sk_X509_push(chain, issuer);\n\n    if (OCSP_basic_verify(basic, chain, NULL, OCSP_NOVERIFY) != 1) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"OCSP_basic_verify() failed\") - errbuf;\n        goto error;\n    }\n\n    id = OCSP_cert_to_id(NULL, cert, issuer);\n    if (id == NULL) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"OCSP_cert_to_id() failed\") - errbuf;\n        goto error;\n    }\n\n    if (OCSP_resp_find_status(basic, id, &n, NULL, NULL,\n                              &thisupdate, &nextupdate)\n        != 1)\n    {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"certificate status not found in the \"\n                                    \"OCSP response\") - errbuf;\n        goto error;\n    }\n\n    if (n != V_OCSP_CERTSTATUS_GOOD) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"certificate status \\\"%s\\\" in the OCSP \"\n                                    \"response\", OCSP_cert_status_str(n))\n                       - errbuf;\n        goto error;\n    }\n\n    if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {\n        *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                    \"OCSP_check_validity() failed\") - errbuf;\n        goto error;\n    }\n\n    if (nextupdate) {\n        *valid = ngx_http_lua_ssl_stapling_time(nextupdate);\n        if (*valid == NGX_ERROR) {\n            *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,\n                                        \"invalid nextUpdate time \"\n                                        \"in certificate status\") - errbuf;\n            goto error;\n        }\n    }\n\n    sk_X509_free(chain);\n    X509_free(cert);\n    X509_free(issuer);\n    BIO_free(bio);\n    OCSP_CERTID_free(id);\n    OCSP_BASICRESP_free(basic);\n    OCSP_RESPONSE_free(ocsp);\n\n    return NGX_OK;\n\nerror:\n\n    if (chain) {\n        sk_X509_free(chain);\n    }\n\n    if (id) {\n        OCSP_CERTID_free(id);\n    }\n\n    if (basic) {\n        OCSP_BASICRESP_free(basic);\n    }\n\n    if (ocsp) {\n        OCSP_RESPONSE_free(ocsp);\n    }\n\n    if (cert) {\n        X509_free(cert);\n    }\n\n    if (issuer) {\n        X509_free(issuer);\n    }\n\n    if (bio) {\n        BIO_free(bio);\n    }\n\n    ERR_clear_error();\n\n    return NGX_ERROR;\n\n#endif  /* NGX_HTTP_LUA_USE_OCSP */\n}\n\n\n#ifdef NGX_HTTP_LUA_USE_OCSP\nstatic int\nngx_http_lua_ssl_empty_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)\n{\n    return SSL_TLSEXT_ERR_OK;\n}\n\n\nstatic long\nngx_http_lua_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time)\n{\n    BIO     *bio;\n    char    *value;\n    size_t   len;\n    time_t   time;\n\n    /*\n     * OpenSSL doesn't provide a way to convert ASN1_GENERALIZEDTIME\n     * into long.  To do this, we use ASN1_GENERALIZEDTIME_print(),\n     * which uses the \"MMM DD HH:MM:SS YYYY [GMT]\" format (e.g.,\n     * \"Feb  3 00:55:52 2015 GMT\"), and parse the result.\n     */\n\n    bio = BIO_new(BIO_s_mem());\n    if (bio == NULL) {\n        return NGX_ERROR;\n    }\n\n    /* fake weekday prepended to match C asctime() format */\n\n    BIO_write(bio, \"Tue \", sizeof(\"Tue \") - 1);\n    ASN1_GENERALIZEDTIME_print(bio, asn1time);\n    len = BIO_get_mem_data(bio, &value);\n\n    time = ngx_parse_http_time((u_char *) value, len);\n\n    BIO_free(bio);\n\n    return time;\n}\n#endif\n\n\nint\nngx_http_lua_ffi_ssl_set_ocsp_status_resp(ngx_http_request_t *r,\n    const u_char *resp, size_t resp_len, char **err)\n{\n#ifndef NGX_HTTP_LUA_USE_OCSP\n\n    *err = \"no OCSP support\";\n    return NGX_ERROR;\n\n#else\n\n    u_char                  *p;\n    SSL_CTX                 *ctx;\n    ngx_ssl_conn_t          *ssl_conn;\n\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = r->connection->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n#ifdef SSL_CTRL_GET_TLSEXT_STATUS_REQ_TYPE\n    if (SSL_get_tlsext_status_type(ssl_conn) == -1) {\n#else\n    if (ssl_conn->tlsext_status_type == -1) {\n#endif\n        dd(\"no ocsp status req from client\");\n        return NGX_DECLINED;\n    }\n\n    /* we have to register an empty status callback here otherwise\n     * OpenSSL won't send the response staple. */\n\n    ctx = SSL_get_SSL_CTX(ssl_conn);\n    SSL_CTX_set_tlsext_status_cb(ctx,\n                                 ngx_http_lua_ssl_empty_status_callback);\n\n    p = OPENSSL_malloc(resp_len);\n    if (p == NULL) {\n        *err = \"OPENSSL_malloc() failed\";\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(p, resp, resp_len);\n\n    dd(\"set ocsp resp: resp_len=%d\", (int) resp_len);\n    (void) SSL_set_tlsext_status_ocsp_resp(ssl_conn, p, resp_len);\n\n    return NGX_OK;\n\n#endif  /* NGX_HTTP_LUA_USE_OCSP */\n}\n\n\n#endif  /* NGX_HTTP_SSL */\n"
  },
  {
    "path": "src/ngx_http_lua_ssl_session_fetchby.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#if (NGX_HTTP_SSL)\n\n\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_initworkerby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_ssl_module.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_ssl_session_fetchby.h\"\n#include \"ngx_http_lua_ssl.h\"\n#include \"ngx_http_lua_directive.h\"\n\n\n/* Lua SSL cached session loading routines */\nstatic void ngx_http_lua_ssl_sess_fetch_done(void *data);\nstatic void ngx_http_lua_ssl_sess_fetch_aborted(void *data);\nstatic u_char *ngx_http_lua_log_ssl_sess_fetch_error(ngx_log_t *log,\n    u_char *buf, size_t len);\nstatic ngx_int_t ngx_http_lua_ssl_sess_fetch_by_chunk(lua_State *L,\n    ngx_http_request_t *r);\n\n\n/* load Lua code from a file for fetching cached SSL session */\nngx_int_t\nngx_http_lua_ssl_sess_fetch_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L,\n                                     lscf->srv.ssl_sess_fetch_src.data,\n                                     &lscf->srv.ssl_sess_fetch_src_ref,\n                                     lscf->srv.ssl_sess_fetch_src_key);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_ssl_sess_fetch_by_chunk(L, r);\n}\n\n\n/* load lua code from an inline snippet for fetching cached SSL session */\nngx_int_t\nngx_http_lua_ssl_sess_fetch_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       lscf->srv.ssl_sess_fetch_src.data,\n                                       lscf->srv.ssl_sess_fetch_src.len,\n                                       &lscf->srv.ssl_sess_fetch_src_ref,\n                                       lscf->srv.ssl_sess_fetch_src_key,\n                             (const char *) lscf->srv.ssl_sess_fetch_chunkname);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_ssl_sess_fetch_by_chunk(L, r);\n}\n\n\nchar *\nngx_http_lua_ssl_sess_fetch_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_ssl_sess_fetch_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\n/* conf parser for directive ssl_session_fetch_by_lua */\nchar *\nngx_http_lua_ssl_sess_fetch_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    size_t                       chunkname_len;\n    u_char                      *chunkname;\n    u_char                      *cache_key = NULL;\n    u_char                      *name;\n    ngx_str_t                   *value;\n    ngx_http_lua_srv_conf_t     *lscf = conf;\n\n    dd(\"enter\");\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (lscf->srv.ssl_sess_fetch_handler) {\n        return \"is duplicate\";\n    }\n\n    if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    lscf->srv.ssl_sess_fetch_handler =\n        (ngx_http_lua_srv_conf_handler_pt) cmd->post;\n\n    if (cmd->post == ngx_http_lua_ssl_sess_fetch_handler_file) {\n        /* Lua code in an external file */\n        name = ngx_http_lua_rebase_path(cf->pool, value[1].data,\n                                        value[1].len);\n        if (name == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                    value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        lscf->srv.ssl_sess_fetch_src.data = name;\n        lscf->srv.ssl_sess_fetch_src.len = ngx_strlen(name);\n\n    } else {\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf,\n                                                     \"ssl_session_fetch_by_lua\",\n                                                     value[1].data,\n                                                     value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"ssl_session_fetch_by_lua\",\n                        sizeof(\"ssl_session_fetch_by_lua\") - 1, &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        lscf->srv.ssl_sess_fetch_src = value[1];\n        lscf->srv.ssl_sess_fetch_chunkname = chunkname;\n    }\n\n    lscf->srv.ssl_sess_fetch_src_key = cache_key;\n\n    return NGX_CONF_OK;\n}\n\n\n/* cached session fetching callback to be set with SSL_CTX_sess_set_get_cb */\nngx_ssl_session_t *\nngx_http_lua_ssl_sess_fetch_handler(ngx_ssl_conn_t *ssl_conn,\n#if OPENSSL_VERSION_NUMBER >= 0x10100003L\n    const\n#endif\n    u_char *id, int len, int *copy)\n{\n#if defined(NGX_SSL_TLSv1_3) && defined(TLS1_3_VERSION)\n    int                              tls_version;\n#endif\n    lua_State                       *L;\n    ngx_int_t                        rc;\n    ngx_connection_t                *c, *fc = NULL;\n    ngx_http_request_t              *r = NULL;\n    ngx_pool_cleanup_t              *cln;\n    ngx_http_connection_t           *hc;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n    ngx_http_lua_srv_conf_t         *lscf;\n    ngx_http_core_loc_conf_t        *clcf;\n\n    /* set copy to 0 as we expect OpenSSL to handle\n     * the memory of returned session */\n\n    *copy = 0;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n#if defined(NGX_SSL_TLSv1_3) && defined(TLS1_3_VERSION)\n    tls_version = SSL_version(ssl_conn);\n\n    if (tls_version >= TLS1_3_VERSION) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"ssl_session_fetch_by_lua*: skipped since \"\n                       \"TLS version >= 1.3 (%xd)\", tls_version);\n\n        return 0;\n    }\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"ssl session fetch: connection reusable: %ud\", c->reusable);\n\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n\n    dd(\"ssl sess_fetch handler, sess_fetch-ctx=%p\", cctx);\n\n    if (cctx && cctx->entered_sess_fetch_handler) {\n        /* not the first time */\n\n        dd(\"here: %d\", (int) cctx->entered_sess_fetch_handler);\n\n        if (cctx->done) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"ssl_session_fetch_by_lua*: \"\n                           \"sess get cb exit code: %d\",\n                           cctx->exit_code);\n\n            dd(\"lua ssl sess_fetch done, finally\");\n            return cctx->session;\n        }\n\n#ifdef SSL_ERROR_PENDING_SESSION\n        return SSL_magic_pending_session_ptr();\n#else\n        ngx_log_error(NGX_LOG_CRIT, c->log, 0,\n                      \"lua: cannot yield in sess get cb: \"\n                      \"missing async sess get cb support in OpenSSL\");\n        return NULL;\n#endif\n    }\n\n    dd(\"first time\");\n\n#if (nginx_version < 1017009)\n    ngx_reusable_connection(c, 0);\n#endif\n\n    hc = c->data;\n\n    fc = ngx_http_lua_create_fake_connection(NULL);\n    if (fc == NULL) {\n        goto failed;\n    }\n\n    fc->log->handler = ngx_http_lua_log_ssl_sess_fetch_error;\n    fc->log->data = fc;\n\n    fc->addr_text = c->addr_text;\n    fc->listening = c->listening;\n\n    r = ngx_http_lua_create_fake_request(fc);\n    if (r == NULL) {\n        goto failed;\n    }\n\n    r->main_conf = hc->conf_ctx->main_conf;\n    r->srv_conf = hc->conf_ctx->srv_conf;\n    r->loc_conf = hc->conf_ctx->loc_conf;\n\n    fc->log->file = c->log->file;\n    fc->log->log_level = c->log->log_level;\n    fc->ssl = c->ssl;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n#if (nginx_version >= 1009000)\n    ngx_set_connection_log(fc, clcf->error_log);\n\n#else\n    ngx_http_set_connection_log(fc, clcf->error_log);\n#endif\n\n    if (cctx == NULL) {\n        cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t));\n        if (cctx == NULL) {\n            goto failed;  /* error */\n        }\n\n        cctx->ctx_ref = LUA_NOREF;\n    }\n\n    cctx->exit_code = 1;  /* successful by default */\n    cctx->connection = c;\n    cctx->request = r;\n    cctx->session_id.data = (u_char *) id;\n    cctx->session_id.len = len;\n    cctx->entered_sess_fetch_handler = 1;\n    cctx->done = 0;\n\n    dd(\"setting cctx = %p\", cctx);\n\n    if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index, cctx)\n        == 0)\n    {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"SSL_set_ex_data() failed\");\n        goto failed;\n    }\n\n    lscf = ngx_http_get_module_srv_conf(r, ngx_http_lua_module);\n\n    /* TODO honor lua_code_cache off */\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    c->log->action = \"fetching SSL session by lua\";\n\n    rc = lscf->srv.ssl_sess_fetch_handler(r, lscf, L);\n\n    if (rc >= NGX_OK || rc == NGX_ERROR) {\n        cctx->done = 1;\n\n        if (cctx->cleanup) {\n            *cctx->cleanup = NULL;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"ssl_session_fetch_by_lua*: handler return value: %i, \"\n                       \"sess get cb exit code: %d\", rc, cctx->exit_code);\n\n        c->log->action = \"SSL handshaking\";\n        return cctx->session;\n    }\n\n    /* rc == NGX_DONE */\n\n    cln = ngx_pool_cleanup_add(fc->pool, 0);\n    if (cln == NULL) {\n        goto failed;\n    }\n\n    cln->handler = ngx_http_lua_ssl_sess_fetch_done;\n    cln->data = cctx;\n\n    if (cctx->cleanup == NULL)  {\n        /* we only want exactly one cleanup handler to be registered with the\n         * connection to clean up cctx when connection is aborted */\n        cln = ngx_pool_cleanup_add(c->pool, 0);\n        if (cln == NULL) {\n            goto failed;\n        }\n\n        cln->data = cctx;\n        cctx->cleanup = &cln->handler;\n    }\n\n    *cctx->cleanup = ngx_http_lua_ssl_sess_fetch_aborted;\n\n#ifdef SSL_ERROR_PENDING_SESSION\n    return SSL_magic_pending_session_ptr();\n#else\n    ngx_log_error(NGX_LOG_CRIT, c->log, 0,\n                  \"lua: cannot yield in sess get cb: \"\n                  \"missing async sess get cb support in OpenSSL\");\n\n    /* fall through to the \"failed\" label below */\n#endif\n\nfailed:\n\n    if (r && r->pool) {\n        ngx_http_lua_free_fake_request(r);\n    }\n\n    if (fc) {\n        ngx_http_lua_close_fake_connection(fc);\n    }\n\n    return NULL;\n}\n\n\nstatic void\nngx_http_lua_ssl_sess_fetch_done(void *data)\n{\n    ngx_connection_t                *c;\n    ngx_http_lua_ssl_ctx_t          *cctx = data;\n\n    dd(\"lua ssl sess_fetch done\");\n\n    if (cctx->aborted) {\n        return;\n    }\n\n    ngx_http_lua_assert(cctx->done == 0);\n\n    cctx->done = 1;\n\n    if (cctx->cleanup) {\n        *cctx->cleanup = NULL;\n    }\n\n    c = cctx->connection;\n\n    c->log->action = \"SSL handshaking\";\n\n    ngx_post_event(c->write, &ngx_posted_events);\n}\n\n\nstatic void\nngx_http_lua_ssl_sess_fetch_aborted(void *data)\n{\n    ngx_http_lua_ssl_ctx_t          *cctx = data;\n\n    dd(\"lua ssl sess_fetch aborted\");\n\n    if (cctx->done) {\n        /* completed successfully already */\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0,\n                   \"ssl_session_fetch_by_lua*: sess_fetch cb aborted\");\n\n    cctx->aborted = 1;\n    cctx->request->connection->ssl = NULL;\n\n    ngx_http_lua_finalize_fake_request(cctx->request, NGX_ERROR);\n}\n\n\nstatic u_char *\nngx_http_lua_log_ssl_sess_fetch_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char              *p;\n    ngx_connection_t    *c;\n\n    if (log->action) {\n        p = ngx_snprintf(buf, len, \" while %s\", log->action);\n        len -= p - buf;\n        buf = p;\n    }\n\n    p = ngx_snprintf(buf, len, \", context: ssl_session_fetch_by_lua*\");\n    len -= p - buf;\n    buf = p;\n\n    c = log->data;\n\n    if (c != NULL) {\n        if (c->addr_text.len) {\n            p = ngx_snprintf(buf, len, \", client: %V\", &c->addr_text);\n            len -= p - buf;\n            buf = p;\n        }\n\n        if (c->listening && c->listening->addr_text.len) {\n            p = ngx_snprintf(buf, len, \", server: %V\",\n                             &c->listening->addr_text);\n            buf = p;\n        }\n    }\n\n    return buf;\n}\n\n\n/* initialize lua coroutine for fetching cached session */\nstatic ngx_int_t\nngx_http_lua_ssl_sess_fetch_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    int                      co_ref;\n    ngx_int_t                rc;\n    lua_State               *co;\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_pool_cleanup_t      *cln;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            rc = NGX_ERROR;\n            ngx_http_lua_finalize_request(r, rc);\n            return rc;\n        }\n\n    } else {\n        dd(\"reset ctx\");\n        ngx_http_lua_reset_ctx(r, L, ctx);\n    }\n\n    ctx->entered_content_phase = 1;\n\n    /*  {{{ new coroutine to handle request */\n    co = ngx_http_lua_new_thread(r, L, &co_ref);\n\n    if (co == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"lua: failed to create new coroutine to handle request\");\n\n        rc = NGX_ERROR;\n        ngx_http_lua_finalize_request(r, rc);\n        return rc;\n    }\n\n    /*  move code closure to new coroutine */\n    lua_xmove(L, co, 1);\n\n#ifndef OPENRESTY_LUAJIT\n    /*  set closure's env table to new coroutine's globals table */\n    ngx_http_lua_get_globals_table(co);\n    lua_setfenv(co, -2);\n#endif\n\n    /* save nginx request in coroutine globals table */\n    ngx_http_lua_set_req(co, r);\n\n    ctx->cur_co_ctx = &ctx->entry_co_ctx;\n    ctx->cur_co_ctx->co = co;\n    ctx->cur_co_ctx->co_ref = co_ref;\n#ifdef NGX_LUA_USE_ASSERT\n    ctx->cur_co_ctx->co_top = 1;\n#endif\n\n    ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);\n\n    /* register request cleanup hooks */\n    if (ctx->cleanup == NULL) {\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n        if (cln == NULL) {\n            rc = NGX_ERROR;\n            ngx_http_lua_finalize_request(r, rc);\n            return rc;\n        }\n\n        cln->handler = ngx_http_lua_request_cleanup_handler;\n        cln->data = ctx;\n        ctx->cleanup = &cln->handler;\n    }\n\n    ctx->context = NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH;\n\n    rc = ngx_http_lua_run_thread(L, r, ctx, 0);\n\n    if (rc == NGX_ERROR || rc >= NGX_OK) {\n        /* do nothing */\n\n    } else if (rc == NGX_AGAIN) {\n        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0);\n\n    } else if (rc == NGX_DONE) {\n        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 1);\n\n    } else {\n        rc = NGX_OK;\n    }\n\n    ngx_http_lua_finalize_request(r, rc);\n    return rc;\n}\n\n\n/* de-serialized a SSL session and set it back to the request at lua context */\nint\nngx_http_lua_ffi_ssl_set_serialized_session(ngx_http_request_t *r,\n    const unsigned char *data, int len, char **err)\n{\n    u_char                          *p;\n    u_char                           buf[NGX_SSL_MAX_SESSION_SIZE];\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n    ngx_ssl_session_t               *session = NULL;\n    ngx_ssl_session_t               *old_session;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n\n    c = r->connection;\n\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(buf, data, len);\n    p = buf;\n    session = d2i_SSL_SESSION(NULL, (const unsigned char **)&p,  len);\n    if (session == NULL) {\n        ERR_clear_error();\n        *err = \"failed to de-serialize session\";\n        return NGX_ERROR;\n    }\n\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n    if (cctx == NULL) {\n        *err = \"bad lua context\";\n        return NGX_ERROR;\n    }\n\n    old_session = cctx->session;\n    cctx->session = session;\n\n    if (old_session != NULL) {\n        ngx_ssl_free_session(old_session);\n    }\n\n    return NGX_OK;\n}\n\n\n#endif /* NGX_HTTP_SSL */\n"
  },
  {
    "path": "src/ngx_http_lua_ssl_session_fetchby.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_SSL_SESSION_FETCHBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_SSL_SESSION_FETCHBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\n#if (NGX_HTTP_SSL)\nngx_int_t ngx_http_lua_ssl_sess_fetch_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L);\n\nngx_int_t ngx_http_lua_ssl_sess_fetch_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L);\n\nchar *ngx_http_lua_ssl_sess_fetch_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nchar *ngx_http_lua_ssl_sess_fetch_by_lua_block(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\n\nngx_ssl_session_t *ngx_http_lua_ssl_sess_fetch_handler(\n    ngx_ssl_conn_t *ssl_conn,\n#if OPENSSL_VERSION_NUMBER >= 0x10100003L\n    const\n#endif\n    u_char *id, int len, int *copy);\n#endif\n\n\n#endif /* _NGX_HTTP_LUA_SSL_SESSION_FETCHBY_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_ssl_session_storeby.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#if (NGX_HTTP_SSL)\n\n\n#include \"ngx_http_lua_cache.h\"\n#include \"ngx_http_lua_initworkerby.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_ssl_module.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_ssl_session_storeby.h\"\n#include \"ngx_http_lua_ssl.h\"\n#include \"ngx_http_lua_directive.h\"\n\n\n/* Lua SSL new session store routines */\nstatic u_char *ngx_http_lua_log_ssl_sess_store_error(ngx_log_t *log,\n    u_char *buf, size_t len);\nstatic ngx_int_t ngx_http_lua_ssl_sess_store_by_chunk(lua_State *L,\n    ngx_http_request_t *r);\n\n\n/* load Lua code from a file for caching new SSL session. */\nngx_int_t\nngx_http_lua_ssl_sess_store_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadfile(r->connection->log, L,\n                                     lscf->srv.ssl_sess_store_src.data,\n                                     &lscf->srv.ssl_sess_store_src_ref,\n                                     lscf->srv.ssl_sess_store_src_key);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_ssl_sess_store_by_chunk(L, r);\n}\n\n\n/* load lua code from an inline snippet for caching new SSL session */\nngx_int_t\nngx_http_lua_ssl_sess_store_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L)\n{\n    ngx_int_t           rc;\n\n    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,\n                                       lscf->srv.ssl_sess_store_src.data,\n                                       lscf->srv.ssl_sess_store_src.len,\n                                       &lscf->srv.ssl_sess_store_src_ref,\n                                       lscf->srv.ssl_sess_store_src_key,\n                             (const char *) lscf->srv.ssl_sess_store_chunkname);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /*  make sure we have a valid code chunk */\n    ngx_http_lua_assert(lua_isfunction(L, -1));\n\n    return ngx_http_lua_ssl_sess_store_by_chunk(L, r);\n}\n\n\nchar *\nngx_http_lua_ssl_sess_store_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char        *rv;\n    ngx_conf_t   save;\n\n    save = *cf;\n    cf->handler = ngx_http_lua_ssl_sess_store_by_lua;\n    cf->handler_conf = conf;\n\n    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);\n\n    *cf = save;\n\n    return rv;\n}\n\n\n/* conf parser for directive ssl_session_store_by_lua */\nchar *\nngx_http_lua_ssl_sess_store_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    size_t                       chunkname_len;\n    u_char                      *chunkname;\n    u_char                      *cache_key = NULL;\n    u_char                      *name;\n    ngx_str_t                   *value;\n    ngx_http_lua_srv_conf_t     *lscf = conf;\n\n    dd(\"enter\");\n\n    /*  must specify a content handler */\n    if (cmd->post == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (lscf->srv.ssl_sess_store_handler) {\n        return \"is duplicate\";\n    }\n\n    if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    lscf->srv.ssl_sess_store_handler =\n        (ngx_http_lua_srv_conf_handler_pt) cmd->post;\n\n    if (cmd->post == ngx_http_lua_ssl_sess_store_handler_file) {\n        /* Lua code in an external file */\n        name = ngx_http_lua_rebase_path(cf->pool, value[1].data,\n                                        value[1].len);\n        if (name == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,\n                                                    value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        lscf->srv.ssl_sess_store_src.data = name;\n        lscf->srv.ssl_sess_store_src.len = ngx_strlen(name);\n\n    } else {\n        cache_key = ngx_http_lua_gen_chunk_cache_key(cf,\n                                                     \"ssl_session_store_by_lua\",\n                                                     value[1].data,\n                                                     value[1].len);\n        if (cache_key == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        chunkname = ngx_http_lua_gen_chunk_name(cf, \"ssl_session_store_by_lua\",\n                        sizeof(\"ssl_session_store_by_lua\") - 1, &chunkname_len);\n        if (chunkname == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* Don't eval nginx variables for inline lua code */\n        lscf->srv.ssl_sess_store_src = value[1];\n        lscf->srv.ssl_sess_store_chunkname = chunkname;\n    }\n\n    lscf->srv.ssl_sess_store_src_key = cache_key;\n\n    return NGX_CONF_OK;\n}\n\n\n/* callback for new session caching, to be set with SSL_CTX_sess_set_new_cb */\nint\nngx_http_lua_ssl_sess_store_handler(ngx_ssl_conn_t *ssl_conn,\n    ngx_ssl_session_t *sess)\n{\n#if defined(NGX_SSL_TLSv1_3) && defined(TLS1_3_VERSION)\n    int                              tls_version;\n#endif\n    const u_char                    *sess_id;\n    unsigned int                     sess_id_len;\n    lua_State                       *L;\n    ngx_int_t                        rc;\n    ngx_connection_t                *c, *fc = NULL;\n    ngx_http_request_t              *r = NULL;\n    ngx_http_connection_t           *hc;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n    ngx_http_lua_srv_conf_t         *lscf;\n    ngx_http_core_loc_conf_t        *clcf;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n#if defined(NGX_SSL_TLSv1_3) && defined(TLS1_3_VERSION)\n    tls_version = SSL_version(ssl_conn);\n\n    if (tls_version >= TLS1_3_VERSION) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"ssl_session_store_by_lua*: skipped since \"\n                       \"TLS version >= 1.3 (%xd)\", tls_version);\n\n        return 0;\n    }\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"ssl session store: connection reusable: %ud\", c->reusable);\n\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n\n    dd(\"ssl sess_store handler, sess_store-ctx=%p\", cctx);\n\n    hc = c->data;\n\n    fc = ngx_http_lua_create_fake_connection(NULL);\n    if (fc == NULL) {\n        goto failed;\n    }\n\n    fc->log->handler = ngx_http_lua_log_ssl_sess_store_error;\n    fc->log->data = fc;\n\n    fc->addr_text = c->addr_text;\n    fc->listening = c->listening;\n\n    r = ngx_http_lua_create_fake_request(fc);\n    if (r == NULL) {\n        goto failed;\n    }\n\n    r->main_conf = hc->conf_ctx->main_conf;\n    r->srv_conf = hc->conf_ctx->srv_conf;\n    r->loc_conf = hc->conf_ctx->loc_conf;\n\n    fc->log->file = c->log->file;\n    fc->log->log_level = c->log->log_level;\n    fc->ssl = c->ssl;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n#if (nginx_version >= 1009000)\n    ngx_set_connection_log(fc, clcf->error_log);\n\n#else\n    ngx_http_set_connection_log(fc, clcf->error_log);\n#endif\n\n    if (cctx == NULL) {\n        cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t));\n        if (cctx == NULL) {\n            goto failed;  /* error */\n        }\n\n        cctx->ctx_ref = LUA_NOREF;\n    }\n\n#if OPENSSL_VERSION_NUMBER >= 0x1000200fL\n    sess_id = SSL_SESSION_get_id(sess, &sess_id_len);\n#else\n    sess_id = sess->session_id;\n    sess_id_len = sess->session_id_length;\n#endif\n\n    cctx->connection = c;\n    cctx->request = r;\n    cctx->session = sess;\n    cctx->session_id.data = (u_char *) sess_id;\n    cctx->session_id.len = sess_id_len;\n    cctx->done = 0;\n\n    dd(\"setting cctx\");\n\n    if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index, cctx)\n        == 0)\n    {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"SSL_set_ex_data() failed\");\n        goto failed;\n    }\n\n    lscf = ngx_http_get_module_srv_conf(r, ngx_http_lua_module);\n\n    /* TODO honor lua_code_cache off */\n    L = ngx_http_lua_get_lua_vm(r, NULL);\n\n    c->log->action = \"storing SSL session by lua\";\n\n    rc = lscf->srv.ssl_sess_store_handler(r, lscf, L);\n\n    if (rc >= NGX_OK || rc == NGX_ERROR) {\n        cctx->done = 1;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"ssl_session_store_by_lua*: handler return value: %i, \"\n                       \"sess new cb exit code: %d\", rc, cctx->exit_code);\n\n        c->log->action = \"SSL handshaking\";\n\n        /* Return value is a flag indicating whether the passed-in session\n         * has been freed by this callback; always return 0 so OpenSSL will\n         * free the session. Nginx's own session caching logic has the same\n         * practice. */\n        return 0;\n    }\n\n    /* impossible to reach here */\n    ngx_http_lua_assert(0);\n\nfailed:\n\n    if (r && r->pool) {\n        ngx_http_lua_free_fake_request(r);\n    }\n\n    if (fc) {\n        ngx_http_lua_close_fake_connection(fc);\n    }\n\n    return 0;\n}\n\n\nstatic u_char *\nngx_http_lua_log_ssl_sess_store_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char              *p;\n    ngx_connection_t    *c;\n\n    if (log->action) {\n        p = ngx_snprintf(buf, len, \" while %s\", log->action);\n        len -= p - buf;\n        buf = p;\n    }\n\n    p = ngx_snprintf(buf, len, \", context: ssl_session_store_by_lua*\");\n    len -= p - buf;\n    buf = p;\n\n    c = log->data;\n\n    if (c != NULL) {\n        if (c->addr_text.len) {\n            p = ngx_snprintf(buf, len, \", client: %V\", &c->addr_text);\n            len -= p - buf;\n            buf = p;\n        }\n\n        if (c->listening && c->listening->addr_text.len) {\n            p = ngx_snprintf(buf, len, \", server: %V\",\n                             &c->listening->addr_text);\n            buf = p;\n        }\n    }\n\n    return buf;\n}\n\n\n/* initialize lua coroutine for caching new SSL session */\nstatic ngx_int_t\nngx_http_lua_ssl_sess_store_by_chunk(lua_State *L, ngx_http_request_t *r)\n{\n    size_t                   len;\n    u_char                  *err_msg;\n    ngx_int_t                rc;\n    ngx_http_lua_ctx_t      *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (ctx == NULL) {\n        ctx = ngx_http_lua_create_ctx(r);\n        if (ctx == NULL) {\n            rc = NGX_ERROR;\n            ngx_http_lua_finalize_request(r, rc);\n            return rc;\n        }\n\n    } else {\n        dd(\"reset ctx\");\n        ngx_http_lua_reset_ctx(r, L, ctx);\n    }\n\n    ctx->entered_content_phase = 1;\n    ctx->context = NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE;\n\n    /* init nginx context in Lua VM */\n    ngx_http_lua_set_req(L, r);\n\n#ifndef OPENRESTY_LUAJIT\n    ngx_http_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);\n\n    /*  {{{ make new env inheriting main thread's globals table */\n    lua_createtable(L, 0, 1 /* nrec */);   /* the metatable for the new env */\n    ngx_http_lua_get_globals_table(L);\n    lua_setfield(L, -2, \"__index\");\n    lua_setmetatable(L, -2);    /*  setmetatable({}, {__index = _G}) */\n    /*  }}} */\n\n    lua_setfenv(L, -2);    /*  set new running env for the code closure */\n#endif\n\n    lua_pushcfunction(L, ngx_http_lua_traceback);\n    lua_insert(L, 1);  /* put it under chunk and args */\n\n    /*  protected call user code */\n    rc = lua_pcall(L, 0, 1, 1);\n\n    lua_remove(L, 1);  /* remove traceback function */\n\n    dd(\"rc == %d\", (int) rc);\n\n    if (rc != 0) {\n        /*  error occurred when running loaded code */\n        err_msg = (u_char *) lua_tolstring(L, -1, &len);\n\n        if (err_msg == NULL) {\n            err_msg = (u_char *) \"unknown reason\";\n            len = sizeof(\"unknown reason\") - 1;\n        }\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"failed to run session_store_by_lua*: %*s\", len, err_msg);\n\n        lua_settop(L, 0); /*  clear remaining elems on stack */\n        ngx_http_lua_finalize_request(r, rc);\n\n        return NGX_ERROR;\n    }\n\n    lua_settop(L, 0); /*  clear remaining elems on stack */\n    ngx_http_lua_finalize_request(r, rc);\n    return rc;\n}\n\n\n/* serialize a session from lua context into buf.\n * the memory allocation of buf should be handled externally. */\nint\nngx_http_lua_ffi_ssl_get_serialized_session(ngx_http_request_t *r,\n    u_char *buf, char **err)\n{\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n    ngx_ssl_session_t               *session;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n\n    c = r->connection;\n\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    dd(\"get cctx session\");\n\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n    if (cctx == NULL) {\n        *err = \"bad lua context\";\n        return NGX_ERROR;\n    }\n\n    session = cctx->session;\n    if (session == NULL) {\n        *err = \"bad session in lua context\";\n        return NGX_ERROR;\n    }\n\n    if (i2d_SSL_SESSION(session, &buf) == 0) {\n        *err = \"i2d_SSL_SESSION() failed\";\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\n/* return the size of serialized session. */\nint\nngx_http_lua_ffi_ssl_get_serialized_session_size(ngx_http_request_t *r,\n    char **err)\n{\n    int                              len;\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n    ngx_ssl_session_t               *session;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n\n    c = r->connection;\n\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    dd(\"get cctx session size\");\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n    if (cctx == NULL) {\n        *err = \"bad lua context\";\n        return NGX_ERROR;\n    }\n\n    session = cctx->session;\n    if (session == NULL) {\n        *err = \"bad session in lua context\";\n        return NGX_ERROR;\n    }\n\n    len = i2d_SSL_SESSION(session, NULL);\n    if (len == 0) {\n        *err = \"i2d_SSL_SESSION() failed\";\n        return NGX_ERROR;\n    }\n\n    return len;\n}\n\n\n/* serialize the session id from lua context into buf.\n * the memory allocation of buf should be handled externally. */\nint\nngx_http_lua_ffi_ssl_get_session_id(ngx_http_request_t *r,\n    u_char *buf, char **err)\n{\n    int                              id_len;\n    u_char                          *id;\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n\n    c = r->connection;\n\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    dd(\"get cctx session\");\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n    if (cctx == NULL) {\n        *err = \"bad lua context\";\n        return NGX_ERROR;\n    }\n\n    id = cctx->session_id.data;\n    if (id == NULL) {\n        *err = \"uninitialized session id in lua context\";\n        return NGX_ERROR;\n    }\n\n    id_len = cctx->session_id.len;\n    if (id_len == 0) {\n        *err = \"uninitialized session id len in lua context\";\n        return NGX_ERROR;\n    }\n\n    ngx_hex_dump(buf, id, id_len);\n\n    return NGX_OK;\n}\n\n\n/* return the size of serialized session id. */\nint\nngx_http_lua_ffi_ssl_get_session_id_size(ngx_http_request_t *r,\n    char **err)\n{\n    ngx_ssl_conn_t                  *ssl_conn;\n    ngx_connection_t                *c;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n\n    c = r->connection;\n\n    if (c == NULL || c->ssl == NULL) {\n        *err = \"bad request\";\n        return NGX_ERROR;\n    }\n\n    ssl_conn = c->ssl->connection;\n    if (ssl_conn == NULL) {\n        *err = \"bad ssl conn\";\n        return NGX_ERROR;\n    }\n\n    dd(\"get cctx session\");\n    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n    if (cctx == NULL) {\n        *err = \"bad lua context\";\n        return NGX_ERROR;\n    }\n\n    if (cctx->session_id.len == 0) {\n        *err = \"uninitialized session id len in lua context\";\n        return NGX_ERROR;\n    }\n\n    /* since the session id will be hex dumped to serialize, the serialized\n     * session will be twice the size of the session id: each byte will be a\n     * 2-digit hex value. */\n\n    return 2 * cctx->session_id.len;\n}\n\n\n#endif /* NGX_HTTP_SSL */\n"
  },
  {
    "path": "src/ngx_http_lua_ssl_session_storeby.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_SSL_SESSION_STOREBY_H_INCLUDED_\n#define _NGX_HTTP_LUA_SSL_SESSION_STOREBY_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\n#if (NGX_HTTP_SSL)\nngx_int_t ngx_http_lua_ssl_sess_store_handler_inline(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L);\n\nngx_int_t ngx_http_lua_ssl_sess_store_handler_file(ngx_http_request_t *r,\n    ngx_http_lua_srv_conf_t *lscf, lua_State *L);\n\nchar *ngx_http_lua_ssl_sess_store_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nchar *ngx_http_lua_ssl_sess_store_by_lua_block(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\n\nint ngx_http_lua_ssl_sess_store_handler(ngx_ssl_conn_t *ssl_conn,\n    ngx_ssl_session_t *sess);\n#endif\n\n\n#endif /* _NGX_HTTP_LUA_SSL_SESSION_STOREBY_H_INCLUDED_  */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_string.c",
    "content": "/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_string.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_args.h\"\n#include \"ngx_crc32.h\"\n\n#if (NGX_HAVE_SHA1)\n#include \"ngx_sha1.h\"\n#endif\n\n#include \"ngx_md5.h\"\n\n#if (NGX_OPENSSL)\n#include <openssl/evp.h>\n#include <openssl/hmac.h>\n#endif\n\n\nstatic uintptr_t ngx_http_lua_ngx_escape_sql_str(u_char *dst, u_char *src,\n    size_t size);\nstatic int ngx_http_lua_ngx_quote_sql_str(lua_State *L);\nstatic int ngx_http_lua_ngx_encode_args(lua_State *L);\nstatic int ngx_http_lua_ngx_decode_args(lua_State *L);\n#if (NGX_OPENSSL)\nstatic int ngx_http_lua_ngx_hmac_sha1(lua_State *L);\n#endif\n\n\nvoid\nngx_http_lua_inject_string_api(lua_State *L)\n{\n    lua_pushcfunction(L, ngx_http_lua_ngx_encode_args);\n    lua_setfield(L, -2, \"encode_args\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_decode_args);\n    lua_setfield(L, -2, \"decode_args\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_quote_sql_str);\n    lua_setfield(L, -2, \"quote_sql_str\");\n\n#if (NGX_OPENSSL)\n    lua_pushcfunction(L, ngx_http_lua_ngx_hmac_sha1);\n    lua_setfield(L, -2, \"hmac_sha1\");\n#endif\n}\n\n\nstatic int\nngx_http_lua_ngx_quote_sql_str(lua_State *L)\n{\n    size_t                   len, dlen, escape;\n    u_char                  *p;\n    u_char                  *src, *dst;\n\n    if (lua_gettop(L) != 1) {\n        return luaL_error(L, \"expecting one argument\");\n    }\n\n    src = (u_char *) luaL_checklstring(L, 1, &len);\n\n    if (len == 0) {\n        dst = (u_char *) \"''\";\n        dlen = sizeof(\"''\") - 1;\n        lua_pushlstring(L, (char *) dst, dlen);\n        return 1;\n    }\n\n    escape = ngx_http_lua_ngx_escape_sql_str(NULL, src, len);\n\n    dlen = sizeof(\"''\") - 1 + len + escape;\n\n    p = lua_newuserdata(L, dlen);\n\n    dst = p;\n\n    *p++ = '\\'';\n\n    if (escape == 0) {\n        p = ngx_copy(p, src, len);\n\n    } else {\n        p = (u_char *) ngx_http_lua_ngx_escape_sql_str(p, src, len);\n    }\n\n    *p++ = '\\'';\n\n    if (p != dst + dlen) {\n        return NGX_ERROR;\n    }\n\n    lua_pushlstring(L, (char *) dst, p - dst);\n\n    return 1;\n}\n\n\nstatic uintptr_t\nngx_http_lua_ngx_escape_sql_str(u_char *dst, u_char *src, size_t size)\n{\n    ngx_uint_t               n;\n\n    if (dst == NULL) {\n        /* find the number of chars to be escaped */\n        n = 0;\n        while (size) {\n            /* the highest bit of all the UTF-8 chars\n             * is always 1 */\n            if ((*src & 0x80) == 0) {\n                switch (*src) {\n                    case '\\0':\n                    case '\\b':\n                    case '\\n':\n                    case '\\r':\n                    case '\\t':\n                    case 26:  /* \\Z */\n                    case '\\\\':\n                    case '\\'':\n                    case '\"':\n                        n++;\n                        break;\n                    default:\n                        break;\n                }\n            }\n\n            src++;\n            size--;\n        }\n\n        return (uintptr_t) n;\n    }\n\n    while (size) {\n        if ((*src & 0x80) == 0) {\n            switch (*src) {\n                case '\\0':\n                    *dst++ = '\\\\';\n                    *dst++ = '0';\n                    break;\n\n                case '\\b':\n                    *dst++ = '\\\\';\n                    *dst++ = 'b';\n                    break;\n\n                case '\\n':\n                    *dst++ = '\\\\';\n                    *dst++ = 'n';\n                    break;\n\n                case '\\r':\n                    *dst++ = '\\\\';\n                    *dst++ = 'r';\n                    break;\n\n                case '\\t':\n                    *dst++ = '\\\\';\n                    *dst++ = 't';\n                    break;\n\n                case 26:\n                    *dst++ = '\\\\';\n                    *dst++ = 'Z';\n                    break;\n\n                case '\\\\':\n                    *dst++ = '\\\\';\n                    *dst++ = '\\\\';\n                    break;\n\n                case '\\'':\n                    *dst++ = '\\\\';\n                    *dst++ = '\\'';\n                    break;\n\n                case '\"':\n                    *dst++ = '\\\\';\n                    *dst++ = '\"';\n                    break;\n\n                default:\n                    *dst++ = *src;\n                    break;\n            }\n\n        } else {\n            *dst++ = *src;\n        }\n\n        src++;\n        size--;\n    } /* while (size) */\n\n    return (uintptr_t) dst;\n}\n\n\nstatic void\nngx_http_lua_encode_base64(ngx_str_t *dst, ngx_str_t *src, int no_padding)\n{\n    u_char         *d, *s;\n    size_t          len;\n    static u_char   basis[] =\n            \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n    len = src->len;\n    s = src->data;\n    d = dst->data;\n\n    while (len > 2) {\n        *d++ = basis[(s[0] >> 2) & 0x3f];\n        *d++ = basis[((s[0] & 3) << 4) | (s[1] >> 4)];\n        *d++ = basis[((s[1] & 0x0f) << 2) | (s[2] >> 6)];\n        *d++ = basis[s[2] & 0x3f];\n\n        s += 3;\n        len -= 3;\n    }\n\n    if (len) {\n        *d++ = basis[(s[0] >> 2) & 0x3f];\n\n        if (len == 1) {\n            *d++ = basis[(s[0] & 3) << 4];\n            if (!no_padding) {\n                *d++ = '=';\n            }\n\n        } else {\n            *d++ = basis[((s[0] & 3) << 4) | (s[1] >> 4)];\n            *d++ = basis[(s[1] & 0x0f) << 2];\n        }\n\n        if (!no_padding) {\n            *d++ = '=';\n        }\n    }\n\n    dst->len = d - dst->data;\n}\n\n\nstatic int\nngx_http_lua_ngx_encode_args(lua_State *L)\n{\n    ngx_str_t                    args;\n\n    if (lua_gettop(L) != 1) {\n        return luaL_error(L, \"expecting 1 argument but seen %d\",\n                          lua_gettop(L));\n    }\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n    ngx_http_lua_process_args_option(NULL, L, 1, &args);\n    lua_pushlstring(L, (char *) args.data, args.len);\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_ngx_decode_args(lua_State *L)\n{\n    u_char                      *buf;\n    u_char                      *tmp;\n    size_t                       len = 0;\n    int                          n;\n    int                          max;\n\n    n = lua_gettop(L);\n\n    if (n != 1 && n != 2) {\n        return luaL_error(L, \"expecting 1 or 2 arguments but seen %d\", n);\n    }\n\n    buf = (u_char *) luaL_checklstring(L, 1, &len);\n\n    if (n == 2) {\n        max = luaL_checkint(L, 2);\n        lua_pop(L, 1);\n\n    } else {\n        max = NGX_HTTP_LUA_MAX_ARGS;\n    }\n\n    tmp = lua_newuserdata(L, len);\n    ngx_memcpy(tmp, buf, len);\n\n    lua_createtable(L, 0, 4);\n\n    return ngx_http_lua_parse_args(L, tmp, tmp + len, max);\n}\n\n\n#if (NGX_OPENSSL)\nstatic int\nngx_http_lua_ngx_hmac_sha1(lua_State *L)\n{\n    u_char                  *sec, *sts;\n    size_t                   lsec, lsts;\n    unsigned int             md_len;\n    unsigned char            md[EVP_MAX_MD_SIZE];\n    const EVP_MD            *evp_md;\n\n    if (lua_gettop(L) != 2) {\n        return luaL_error(L, \"expecting 2 arguments, but got %d\",\n                          lua_gettop(L));\n    }\n\n    sec = (u_char *) luaL_checklstring(L, 1, &lsec);\n    sts = (u_char *) luaL_checklstring(L, 2, &lsts);\n\n    evp_md = EVP_sha1();\n\n    HMAC(evp_md, sec, lsec, sts, lsts, md, &md_len);\n\n    lua_pushlstring(L, (char *) md, md_len);\n\n    return 1;\n}\n#endif\n\n\nvoid\nngx_http_lua_ffi_md5_bin(const u_char *src, size_t len, u_char *dst)\n{\n    ngx_md5_t     md5;\n\n    ngx_md5_init(&md5);\n    ngx_md5_update(&md5, src, len);\n    ngx_md5_final(dst, &md5);\n}\n\n\nvoid\nngx_http_lua_ffi_md5(const u_char *src, size_t len, u_char *dst)\n{\n    ngx_md5_t           md5;\n    u_char              md5_buf[MD5_DIGEST_LENGTH];\n\n    ngx_md5_init(&md5);\n    ngx_md5_update(&md5, src, len);\n    ngx_md5_final(md5_buf, &md5);\n\n    ngx_hex_dump(dst, md5_buf, sizeof(md5_buf));\n}\n\n\nint\nngx_http_lua_ffi_sha1_bin(const u_char *src, size_t len, u_char *dst)\n{\n#if (NGX_HAVE_SHA1)\n    ngx_sha1_t               sha;\n\n    ngx_sha1_init(&sha);\n    ngx_sha1_update(&sha, src, len);\n    ngx_sha1_final(dst, &sha);\n\n    return 1;\n#else\n    return 0;\n#endif\n}\n\n\nunsigned int\nngx_http_lua_ffi_crc32_short(const u_char *src, size_t len)\n{\n    return ngx_crc32_short((u_char *) src, len);\n}\n\n\nunsigned int\nngx_http_lua_ffi_crc32_long(const u_char *src, size_t len)\n{\n    return ngx_crc32_long((u_char *) src, len);\n}\n\n\nsize_t\nngx_http_lua_ffi_encode_base64(const u_char *src, size_t slen, u_char *dst,\n    int no_padding)\n{\n    ngx_str_t      in, out;\n\n    in.data = (u_char *) src;\n    in.len = slen;\n\n    out.data = dst;\n\n    ngx_http_lua_encode_base64(&out, &in, no_padding);\n\n    return out.len;\n}\n\n\nint\nngx_http_lua_ffi_decode_base64(const u_char *src, size_t slen, u_char *dst,\n    size_t *dlen)\n{\n    ngx_int_t      rc;\n    ngx_str_t      in, out;\n\n    in.data = (u_char *) src;\n    in.len = slen;\n\n    out.data = dst;\n\n    rc = ngx_decode_base64(&out, &in);\n\n    *dlen = out.len;\n\n    return rc == NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_decode_base64mime(const u_char *src, size_t slen, u_char *dst,\n    size_t *dlen)\n{\n    ngx_int_t      rc;\n    ngx_str_t      in, out;\n\n    in.data = (u_char *) src;\n    in.len = slen;\n\n    out.data = dst;\n\n    rc = ngx_http_lua_decode_base64mime(&out, &in);\n\n    *dlen = out.len;\n\n    return rc == NGX_OK;\n}\n\n\nsize_t\nngx_http_lua_ffi_unescape_uri(const u_char *src, size_t len, u_char *dst)\n{\n    u_char      *p = dst;\n\n    ngx_http_lua_unescape_uri(&p, (u_char **) &src, len,\n                              NGX_UNESCAPE_URI_COMPONENT);\n    return p - dst;\n}\n\n\nsize_t\nngx_http_lua_ffi_uri_escaped_length(const u_char *src, size_t len,\n    int type)\n{\n    return len + 2 * ngx_http_lua_escape_uri(NULL, (u_char *) src, len, type);\n}\n\n\nvoid\nngx_http_lua_ffi_escape_uri(const u_char *src, size_t len, u_char *dst,\n    int type)\n{\n    ngx_http_lua_escape_uri(dst, (u_char *) src, len, type);\n}\n\n\nvoid\nngx_http_lua_ffi_str_replace_char(u_char *buf, size_t len, const u_char find,\n    const u_char replace)\n{\n    while (len) {\n        if (*buf == find) {\n            *buf = replace;\n        }\n\n        buf++;\n        len--;\n    }\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_string.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_STRING_H_INCLUDED_\n#define _NGX_HTTP_LUA_STRING_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_string_api(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_STRING_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_subrequest.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_subrequest.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_ctx.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_headers_in.h\"\n#if defined(NGX_DTRACE) && NGX_DTRACE\n#include \"ngx_http_probe.h\"\n#endif\n\n\n#define NGX_HTTP_LUA_SHARE_ALL_VARS     0x01\n#define NGX_HTTP_LUA_COPY_ALL_VARS      0x02\n\n\n#define ngx_http_lua_method_name(m) { sizeof(m) - 1, (u_char *) m \" \" }\n\n\nngx_str_t  ngx_http_lua_get_method = ngx_http_lua_method_name(\"GET\");\nngx_str_t  ngx_http_lua_put_method = ngx_http_lua_method_name(\"PUT\");\nngx_str_t  ngx_http_lua_post_method = ngx_http_lua_method_name(\"POST\");\nngx_str_t  ngx_http_lua_head_method = ngx_http_lua_method_name(\"HEAD\");\nngx_str_t  ngx_http_lua_delete_method =\n        ngx_http_lua_method_name(\"DELETE\");\nngx_str_t  ngx_http_lua_options_method =\n        ngx_http_lua_method_name(\"OPTIONS\");\nngx_str_t  ngx_http_lua_copy_method = ngx_http_lua_method_name(\"COPY\");\nngx_str_t  ngx_http_lua_move_method = ngx_http_lua_method_name(\"MOVE\");\nngx_str_t  ngx_http_lua_lock_method = ngx_http_lua_method_name(\"LOCK\");\nngx_str_t  ngx_http_lua_mkcol_method =\n        ngx_http_lua_method_name(\"MKCOL\");\nngx_str_t  ngx_http_lua_propfind_method =\n        ngx_http_lua_method_name(\"PROPFIND\");\nngx_str_t  ngx_http_lua_proppatch_method =\n        ngx_http_lua_method_name(\"PROPPATCH\");\nngx_str_t  ngx_http_lua_unlock_method =\n        ngx_http_lua_method_name(\"UNLOCK\");\nngx_str_t  ngx_http_lua_patch_method =\n        ngx_http_lua_method_name(\"PATCH\");\nngx_str_t  ngx_http_lua_trace_method =\n        ngx_http_lua_method_name(\"TRACE\");\n\nngx_str_t host_header = ngx_string(\"host\");\n\n\nstatic ngx_str_t  ngx_http_lua_content_length_header_key =\n    ngx_string(\"Content-Length\");\n\n\nstatic ngx_int_t ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr,\n    ngx_uint_t method, int forward_body,\n    ngx_http_request_body_t *body, unsigned vars_action,\n    ngx_array_t *extra_vars, ngx_array_t *extra_headers);\nstatic int ngx_http_lua_ngx_location_capture(lua_State *L);\nstatic int ngx_http_lua_ngx_location_capture_multi(lua_State *L);\nstatic void ngx_http_lua_process_keyval_option(ngx_http_request_t *r,\n    lua_State *L, int table, ngx_array_t **varsp);\nstatic ngx_int_t ngx_http_lua_subrequest_add_extra_vars(ngx_http_request_t *r,\n    ngx_array_t *extra_vars);\nstatic ngx_int_t ngx_http_lua_subrequest(ngx_http_request_t *r,\n    ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,\n    ngx_http_post_subrequest_t *ps, ngx_uint_t flags);\nstatic ngx_int_t ngx_http_lua_subrequest_resume(ngx_http_request_t *r);\nstatic void ngx_http_lua_handle_subreq_responses(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx);\nstatic void ngx_http_lua_cancel_subreq(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_post_request_to_head(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_lua_copy_in_file_request_body(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_lua_copy_request_headers(ngx_http_request_t *sr,\n    ngx_http_request_t *pr, int pr_not_chunked, ngx_array_t *extra_headers);\n\n\nenum {\n    NGX_HTTP_LUA_SUBREQ_TRUNCATED = 1,\n};\n\n\n/* ngx.location.capture is just a thin wrapper around\n * ngx.location.capture_multi */\nstatic int\nngx_http_lua_ngx_location_capture(lua_State *L)\n{\n    int                 n;\n\n    n = lua_gettop(L);\n\n    if (n != 1 && n != 2) {\n        return luaL_error(L, \"expecting one or two arguments\");\n    }\n\n    lua_createtable(L, n, 0); /* uri opts? table  */\n    lua_insert(L, 1); /* table uri opts? */\n    if (n == 1) { /* table uri */\n        lua_rawseti(L, 1, 1); /* table */\n\n    } else { /* table uri opts */\n        lua_rawseti(L, 1, 2); /* table uri */\n        lua_rawseti(L, 1, 1); /* table */\n    }\n\n    lua_createtable(L, 1, 0); /* table table' */\n    lua_insert(L, 1);   /* table' table */\n    lua_rawseti(L, 1, 1); /* table' */\n\n    return ngx_http_lua_ngx_location_capture_multi(L);\n}\n\n\nstatic int\nngx_http_lua_ngx_location_capture_multi(lua_State *L)\n{\n    ngx_http_request_t              *r;\n    ngx_http_request_t              *sr = NULL; /* subrequest object */\n    ngx_http_post_subrequest_t      *psr;\n    ngx_http_lua_ctx_t              *sr_ctx;\n    ngx_http_lua_ctx_t              *ctx;\n    ngx_array_t                     *extra_vars;\n    ngx_array_t                     *extra_headers;\n    ngx_str_t                        uri;\n    ngx_str_t                        args;\n    ngx_str_t                        extra_args;\n    ngx_uint_t                       flags;\n    u_char                          *p;\n    u_char                          *q;\n    size_t                           len;\n    size_t                           nargs;\n    int                              rc;\n    int                              n;\n    int                              always_forward_body = 0;\n    ngx_uint_t                       method;\n    ngx_http_request_body_t         *body;\n    int                              type;\n    ngx_buf_t                       *b;\n    unsigned                         vars_action;\n    ngx_uint_t                       nsubreqs;\n    ngx_uint_t                       index;\n    size_t                           sr_statuses_len;\n    size_t                           sr_headers_len;\n    size_t                           sr_bodies_len;\n    size_t                           sr_flags_len;\n    size_t                           ofs1, ofs2;\n    unsigned                         custom_ctx;\n    ngx_http_lua_co_ctx_t           *coctx;\n\n    ngx_http_lua_post_subrequest_data_t      *psr_data;\n\n    n = lua_gettop(L);\n    if (n != 1) {\n        return luaL_error(L, \"only one argument is expected, but got %d\", n);\n    }\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    nsubreqs = lua_objlen(L, 1);\n    if (nsubreqs == 0) {\n        return luaL_error(L, \"at least one subrequest should be specified\");\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request object found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_ACCESS\n                               | NGX_HTTP_LUA_CONTEXT_CONTENT);\n\n    coctx = ctx->cur_co_ctx;\n    if (coctx == NULL) {\n        return luaL_error(L, \"no co ctx found\");\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua location capture, uri:\\\"%V\\\" c:%ud\", &r->uri,\n                   r->main->count);\n\n    sr_statuses_len = nsubreqs * sizeof(ngx_int_t);\n    sr_headers_len  = nsubreqs * sizeof(ngx_http_headers_out_t *);\n    sr_bodies_len   = nsubreqs * sizeof(ngx_str_t);\n    sr_flags_len    = nsubreqs * sizeof(uint8_t);\n\n    p = ngx_pcalloc(r->pool, sr_statuses_len + sr_headers_len +\n                    sr_bodies_len + sr_flags_len);\n\n    if (p == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    coctx->sr_statuses = (void *) p;\n    p += sr_statuses_len;\n\n    coctx->sr_headers = (void *) p;\n    p += sr_headers_len;\n\n    coctx->sr_bodies = (void *) p;\n    p += sr_bodies_len;\n\n    coctx->sr_flags = (void *) p;\n\n    coctx->nsubreqs = nsubreqs;\n\n    coctx->pending_subreqs = 0;\n\n    extra_vars = NULL;\n    extra_headers = NULL;\n\n    for (index = 0; index < nsubreqs; index++) {\n        coctx->pending_subreqs++;\n\n        lua_rawgeti(L, 1, index + 1);\n        if (lua_isnil(L, -1)) {\n            return luaL_error(L, \"only array-like tables are allowed\");\n        }\n\n        dd(\"queries query: top %d\", lua_gettop(L));\n\n        if (lua_type(L, -1) != LUA_TTABLE) {\n            return luaL_error(L, \"the query argument %d is not a table, \"\n                              \"but a %s\",\n                              index, lua_typename(L, lua_type(L, -1)));\n        }\n\n        nargs = lua_objlen(L, -1);\n\n        if (nargs != 1 && nargs != 2) {\n            return luaL_error(L, \"query argument %d expecting one or \"\n                              \"two arguments\", index);\n        }\n\n        lua_rawgeti(L, 2, 1); /* queries query uri */\n\n        dd(\"queries query uri: %d\", lua_gettop(L));\n\n        dd(\"first arg in first query: %s\", lua_typename(L, lua_type(L, -1)));\n\n        body = NULL;\n\n        ngx_str_null(&extra_args);\n\n        if (extra_vars != NULL) {\n            /* flush out existing elements in the array */\n            extra_vars->nelts = 0;\n        }\n\n        if (extra_headers != NULL) {\n            /* flush out existing elements in the array */\n            extra_headers->nelts = 0;\n        }\n\n        vars_action = 0;\n\n        custom_ctx = 0;\n\n        if (nargs == 2) {\n            /* check out the options table */\n\n            lua_rawgeti(L, 2, 2); /* queries query uri opts */\n\n            dd(\"queries query uri opts: %d\", lua_gettop(L));\n\n            if (lua_type(L, 4) != LUA_TTABLE) {\n                return luaL_error(L, \"expecting table as the 2nd argument for \"\n                                  \"subrequest %d, but got %s\", index,\n                                  luaL_typename(L, 4));\n            }\n\n            dd(\"queries query uri opts: %d\", lua_gettop(L));\n\n            /* check the args option */\n\n            lua_getfield(L, 4, \"args\");\n\n            type = lua_type(L, -1);\n\n            switch (type) {\n            case LUA_TTABLE:\n                ngx_http_lua_process_args_option(r, L, -1, &extra_args);\n                break;\n\n            case LUA_TNIL:\n                /* do nothing */\n                break;\n\n            case LUA_TNUMBER:\n            case LUA_TSTRING:\n                extra_args.data = (u_char *) lua_tolstring(L, -1, &len);\n                extra_args.len = len;\n\n                break;\n\n            default:\n                return luaL_error(L, \"Bad args option value\");\n            }\n\n            lua_pop(L, 1);\n\n            dd(\"queries query uri opts: %d\", lua_gettop(L));\n\n            /* check the vars option */\n\n            lua_getfield(L, 4, \"vars\");\n\n            switch (lua_type(L, -1)) {\n            case LUA_TTABLE:\n                ngx_http_lua_process_keyval_option(r, L, -1, &extra_vars);\n\n                dd(\"post process vars top: %d\", lua_gettop(L));\n                break;\n\n            case LUA_TNIL:\n                /* do nothing */\n                break;\n\n            default:\n                return luaL_error(L, \"Bad vars option value\");\n            }\n\n            lua_pop(L, 1);\n\n            dd(\"queries query uri opts: %d\", lua_gettop(L));\n\n            /* check the headers option */\n\n            lua_getfield(L, 4, \"headers\");\n\n            switch (lua_type(L, -1)) {\n            case LUA_TTABLE:\n                ngx_http_lua_process_keyval_option(r, L, -1, &extra_headers);\n\n                dd(\"post process vars top: %d\", lua_gettop(L));\n                break;\n\n            case LUA_TNIL:\n                /* do nothing */\n                break;\n\n            default:\n                return luaL_error(L, \"Bad headers option value\");\n            }\n\n            lua_pop(L, 1); /* pop the headers */\n\n            dd(\"queries query uri opts: %d\", lua_gettop(L));\n\n            /* check the share_all_vars option */\n\n            lua_getfield(L, 4, \"share_all_vars\");\n\n            switch (lua_type(L, -1)) {\n            case LUA_TNIL:\n                /* do nothing */\n                break;\n\n            case LUA_TBOOLEAN:\n                if (lua_toboolean(L, -1)) {\n                    vars_action |= NGX_HTTP_LUA_SHARE_ALL_VARS;\n                }\n                break;\n\n            default:\n                return luaL_error(L, \"Bad share_all_vars option value\");\n            }\n\n            lua_pop(L, 1);\n\n            dd(\"queries query uri opts: %d\", lua_gettop(L));\n\n            /* check the copy_all_vars option */\n\n            lua_getfield(L, 4, \"copy_all_vars\");\n\n            switch (lua_type(L, -1)) {\n            case LUA_TNIL:\n                /* do nothing */\n                break;\n\n            case LUA_TBOOLEAN:\n                if (lua_toboolean(L, -1)) {\n                    vars_action |= NGX_HTTP_LUA_COPY_ALL_VARS;\n                }\n                break;\n\n            default:\n                return luaL_error(L, \"Bad copy_all_vars option value\");\n            }\n\n            lua_pop(L, 1);\n\n            dd(\"queries query uri opts: %d\", lua_gettop(L));\n\n            /* check the \"forward_body\" option */\n\n            lua_getfield(L, 4, \"always_forward_body\");\n            always_forward_body = lua_toboolean(L, -1);\n            lua_pop(L, 1);\n\n            dd(\"always forward body: %d\", always_forward_body);\n\n            /* check the \"method\" option */\n\n            lua_getfield(L, 4, \"method\");\n\n            type = lua_type(L, -1);\n\n            if (type == LUA_TNIL) {\n                method = NGX_HTTP_GET;\n\n            } else {\n                if (type != LUA_TNUMBER) {\n                    return luaL_error(L, \"Bad http request method\");\n                }\n\n                method = (ngx_uint_t) lua_tonumber(L, -1);\n            }\n\n            lua_pop(L, 1);\n\n            dd(\"queries query uri opts: %d\", lua_gettop(L));\n\n            /* check the \"ctx\" option */\n\n            lua_getfield(L, 4, \"ctx\");\n\n            type = lua_type(L, -1);\n\n            if (type != LUA_TNIL) {\n                if (type != LUA_TTABLE) {\n                    return luaL_error(L, \"Bad ctx option value type %s, \"\n                                      \"expected a Lua table\",\n                                      lua_typename(L, type));\n                }\n\n                custom_ctx = 1;\n\n            } else {\n                lua_pop(L, 1);\n            }\n\n            dd(\"queries query uri opts ctx?: %d\", lua_gettop(L));\n\n            /* check the \"body\" option */\n\n            lua_getfield(L, 4, \"body\");\n\n            type = lua_type(L, -1);\n\n            if (type != LUA_TNIL) {\n                if (type != LUA_TSTRING && type != LUA_TNUMBER) {\n                    return luaL_error(L, \"Bad http request body\");\n                }\n\n                body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));\n\n                if (body == NULL) {\n                    return luaL_error(L, \"no memory\");\n                }\n\n                q = (u_char *) lua_tolstring(L, -1, &len);\n\n                dd(\"request body: [%.*s]\", (int) len, q);\n\n                if (len) {\n                    b = ngx_create_temp_buf(r->pool, len);\n                    if (b == NULL) {\n                        return luaL_error(L, \"no memory\");\n                    }\n\n                    b->last = ngx_copy(b->last, q, len);\n\n                    body->bufs = ngx_alloc_chain_link(r->pool);\n                    if (body->bufs == NULL) {\n                        return luaL_error(L, \"no memory\");\n                    }\n\n                    body->bufs->buf = b;\n                    body->bufs->next = NULL;\n\n                    body->buf = b;\n                }\n            }\n\n            lua_pop(L, 1); /* pop the body */\n\n            /* stack: queries query uri opts ctx? */\n\n            lua_remove(L, 4);\n\n            /* stack: queries query uri ctx? */\n\n            dd(\"queries query uri ctx?: %d\", lua_gettop(L));\n\n        } else {\n            method = NGX_HTTP_GET;\n        }\n\n        /* stack: queries query uri ctx? */\n\n        p = (u_char *) luaL_checklstring(L, 3, &len);\n\n        uri.data = ngx_palloc(r->pool, len);\n        if (uri.data == NULL) {\n            return luaL_error(L, \"memory allocation error\");\n        }\n\n        ngx_memcpy(uri.data, p, len);\n\n        uri.len = len;\n\n        ngx_str_null(&args);\n\n        flags = 0;\n\n        rc = ngx_http_parse_unsafe_uri(r, &uri, &args, &flags);\n        if (rc != NGX_OK) {\n            dd(\"rc = %d\", (int) rc);\n\n            return luaL_error(L, \"unsafe uri in argument #1: %s\", p);\n        }\n\n        if (args.len == 0) {\n            if (extra_args.len) {\n                p = ngx_palloc(r->pool, extra_args.len);\n                if (p == NULL) {\n                    return luaL_error(L, \"no memory\");\n                }\n\n                ngx_memcpy(p, extra_args.data, extra_args.len);\n\n                args.data = p;\n                args.len = extra_args.len;\n            }\n\n        } else if (extra_args.len) {\n            /* concatenate the two parts of args together */\n            len = args.len + (sizeof(\"&\") - 1) + extra_args.len;\n\n            p = ngx_palloc(r->pool, len);\n            if (p == NULL) {\n                return luaL_error(L, \"no memory\");\n            }\n\n            q = ngx_copy(p, args.data, args.len);\n            *q++ = '&';\n            ngx_memcpy(q, extra_args.data, extra_args.len);\n\n            args.data = p;\n            args.len = len;\n        }\n\n        ofs1 = ngx_align(sizeof(ngx_http_post_subrequest_t), sizeof(void *));\n        ofs2 = ngx_align(sizeof(ngx_http_lua_ctx_t), sizeof(void *));\n\n        p = ngx_palloc(r->pool, ofs1 + ofs2\n                       + sizeof(ngx_http_lua_post_subrequest_data_t));\n        if (p == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        psr = (ngx_http_post_subrequest_t *) p;\n\n        p += ofs1;\n\n        sr_ctx = (ngx_http_lua_ctx_t *) p;\n\n        ngx_http_lua_assert((void *) sr_ctx == ngx_align_ptr(sr_ctx,\n                                                             sizeof(void *)));\n\n        p += ofs2;\n\n        psr_data = (ngx_http_lua_post_subrequest_data_t *) p;\n\n        ngx_http_lua_assert((void *) psr_data == ngx_align_ptr(psr_data,\n                                                               sizeof(void *)));\n\n        ngx_memzero(sr_ctx, sizeof(ngx_http_lua_ctx_t));\n\n        /* set by ngx_memzero:\n         *      sr_ctx->run_post_subrequest = 0\n         *      sr_ctx->free = NULL\n         *      sr_ctx->body = NULL\n         */\n\n        psr_data->ctx = sr_ctx;\n        psr_data->pr_co_ctx = coctx;\n\n        psr->handler = ngx_http_lua_post_subrequest;\n        psr->data = psr_data;\n\n        rc = ngx_http_lua_subrequest(r, &uri, &args, &sr, psr, 0);\n\n        if (rc != NGX_OK) {\n            return luaL_error(L, \"failed to issue subrequest: %d\", (int) rc);\n        }\n\n        ngx_http_lua_init_ctx(sr, sr_ctx);\n\n        sr_ctx->capture = 1;\n        sr_ctx->index = index;\n        sr_ctx->last_body = &sr_ctx->body;\n        sr_ctx->vm_state = ctx->vm_state;\n\n        ngx_http_set_ctx(sr, sr_ctx, ngx_http_lua_module);\n\n        rc = ngx_http_lua_adjust_subrequest(sr, method, always_forward_body,\n                                            body, vars_action, extra_vars,\n                                            extra_headers);\n\n        if (rc != NGX_OK) {\n            ngx_http_lua_cancel_subreq(sr);\n            return luaL_error(L, \"failed to adjust the subrequest: %d\",\n                              (int) rc);\n        }\n\n        dd(\"queries query uri opts ctx? %d\", lua_gettop(L));\n\n        /* stack: queries query uri ctx? */\n\n        if (custom_ctx) {\n            ngx_http_lua_ngx_set_ctx_helper(L, sr, sr_ctx, -1);\n            lua_pop(L, 3);\n\n        } else {\n            lua_pop(L, 2);\n        }\n\n        /* stack: queries */\n    }\n\n    if (extra_vars) {\n        ngx_array_destroy(extra_vars);\n    }\n\n    ctx->no_abort = 1;\n\n    return lua_yield(L, 0);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method,\n    int always_forward_body, ngx_http_request_body_t *body,\n    unsigned vars_action, ngx_array_t *extra_vars, ngx_array_t *extra_headers)\n{\n    ngx_http_request_t          *r;\n    ngx_http_core_main_conf_t   *cmcf;\n    int                          pr_not_chunked = 0;\n    size_t                       size;\n\n    r = sr->parent;\n\n    sr->header_in = r->header_in;\n\n    if (body) {\n        sr->request_body = body;\n\n    } else if (!always_forward_body\n               && method != NGX_HTTP_PUT\n               && method != NGX_HTTP_POST\n               && r->headers_in.content_length_n > 0)\n    {\n        sr->request_body = NULL;\n\n    } else {\n        if (!r->headers_in.chunked) {\n            pr_not_chunked = 1;\n        }\n\n        if (sr->request_body && sr->request_body->temp_file) {\n\n            /* deep-copy the request body */\n\n            if (ngx_http_lua_copy_in_file_request_body(sr) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    if (ngx_http_lua_copy_request_headers(sr, r, pr_not_chunked, extra_headers)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    sr->method = method;\n\n    switch (method) {\n        case NGX_HTTP_GET:\n            sr->method_name = ngx_http_lua_get_method;\n            break;\n\n        case NGX_HTTP_POST:\n            sr->method_name = ngx_http_lua_post_method;\n            break;\n\n        case NGX_HTTP_PUT:\n            sr->method_name = ngx_http_lua_put_method;\n            break;\n\n        case NGX_HTTP_HEAD:\n            sr->method_name = ngx_http_lua_head_method;\n            break;\n\n        case NGX_HTTP_DELETE:\n            sr->method_name = ngx_http_lua_delete_method;\n            break;\n\n        case NGX_HTTP_OPTIONS:\n            sr->method_name = ngx_http_lua_options_method;\n            break;\n\n        case NGX_HTTP_MKCOL:\n            sr->method_name = ngx_http_lua_mkcol_method;\n            break;\n\n        case NGX_HTTP_COPY:\n            sr->method_name = ngx_http_lua_copy_method;\n            break;\n\n        case NGX_HTTP_MOVE:\n            sr->method_name = ngx_http_lua_move_method;\n            break;\n\n        case NGX_HTTP_PROPFIND:\n            sr->method_name = ngx_http_lua_propfind_method;\n            break;\n\n        case NGX_HTTP_PROPPATCH:\n            sr->method_name = ngx_http_lua_proppatch_method;\n            break;\n\n        case NGX_HTTP_LOCK:\n            sr->method_name = ngx_http_lua_lock_method;\n            break;\n\n        case NGX_HTTP_UNLOCK:\n            sr->method_name = ngx_http_lua_unlock_method;\n            break;\n\n        case NGX_HTTP_PATCH:\n            sr->method_name = ngx_http_lua_patch_method;\n            break;\n\n        case NGX_HTTP_TRACE:\n            sr->method_name = ngx_http_lua_trace_method;\n            break;\n\n        default:\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"unsupported HTTP method: %ui\", method);\n\n            return NGX_ERROR;\n    }\n\n    if (!(vars_action & NGX_HTTP_LUA_SHARE_ALL_VARS)) {\n        /* we do not inherit the parent request's variables */\n        cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module);\n\n        size = cmcf->variables.nelts * sizeof(ngx_http_variable_value_t);\n\n        if (vars_action & NGX_HTTP_LUA_COPY_ALL_VARS) {\n\n            sr->variables = ngx_palloc(sr->pool, size);\n            if (sr->variables == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(sr->variables, r->variables, size);\n\n        } else {\n\n            /* we do not inherit the parent request's variables */\n\n            sr->variables = ngx_pcalloc(sr->pool, size);\n            if (sr->variables == NULL) {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    return ngx_http_lua_subrequest_add_extra_vars(sr, extra_vars);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_subrequest_add_extra_vars(ngx_http_request_t *sr,\n    ngx_array_t *extra_vars)\n{\n    ngx_http_core_main_conf_t   *cmcf;\n    ngx_http_variable_t         *v;\n    ngx_http_variable_value_t   *vv;\n    u_char                      *val;\n    u_char                      *p;\n    ngx_uint_t                   i, hash;\n    ngx_str_t                    name;\n    size_t                       len;\n    ngx_hash_t                  *variables_hash;\n    ngx_keyval_t                *var;\n\n    /* set any extra variables that were passed to the subrequest */\n\n    if (extra_vars == NULL || extra_vars->nelts == 0) {\n        return NGX_OK;\n    }\n\n    cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module);\n\n    variables_hash = &cmcf->variables_hash;\n\n    var = extra_vars->elts;\n\n    for (i = 0; i < extra_vars->nelts; i++, var++) {\n        /* copy the variable's name and value because they are allocated\n         * by the lua VM */\n\n        len = var->key.len + var->value.len;\n\n        p = ngx_pnalloc(sr->pool, len);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        name.data = p;\n        name.len = var->key.len;\n\n        p = ngx_copy(p, var->key.data, var->key.len);\n\n        hash = ngx_hash_strlow(name.data, name.data, name.len);\n\n        val = p;\n        len = var->value.len;\n\n        ngx_memcpy(p, var->value.data, len);\n\n        v = ngx_hash_find(variables_hash, hash, name.data, name.len);\n\n        if (v) {\n            if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {\n                ngx_log_error(NGX_LOG_ERR, sr->connection->log, 0,\n                              \"variable \\\"%V\\\" not changeable\", &name);\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            if (v->set_handler) {\n                vv = ngx_palloc(sr->pool, sizeof(ngx_http_variable_value_t));\n                if (vv == NULL) {\n                    return NGX_ERROR;\n                }\n\n                vv->valid = 1;\n                vv->not_found = 0;\n                vv->no_cacheable = 0;\n\n                vv->data = val;\n                vv->len = len;\n\n                v->set_handler(sr, vv, v->data);\n\n                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sr->connection->log, 0,\n                               \"variable \\\"%V\\\" set to value \\\"%v\\\"\", &name,\n                               vv);\n\n                continue;\n            }\n\n            if (v->flags & NGX_HTTP_VAR_INDEXED) {\n                vv = &sr->variables[v->index];\n\n                vv->valid = 1;\n                vv->not_found = 0;\n                vv->no_cacheable = 0;\n\n                vv->data = val;\n                vv->len = len;\n\n                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sr->connection->log, 0,\n                               \"variable \\\"%V\\\" set to value \\\"%v\\\"\",\n                               &name, vv);\n\n                continue;\n            }\n        }\n\n        ngx_log_error(NGX_LOG_ERR, sr->connection->log, 0,\n                      \"variable \\\"%V\\\" cannot be assigned a value (maybe you \"\n                      \"forgot to define it first?) \", &name);\n\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_lua_process_keyval_option(ngx_http_request_t *r, lua_State *L,\n    int table, ngx_array_t **varsp)\n{\n    ngx_array_t         *vars;\n    ngx_keyval_t        *var;\n\n    if (table < 0) {\n        table = lua_gettop(L) + table + 1;\n    }\n\n    vars = *varsp;\n\n    if (vars == NULL) {\n\n        vars = ngx_array_create(r->pool, 4, sizeof(ngx_keyval_t));\n        if (vars == NULL) {\n            dd(\"here\");\n            luaL_error(L, \"no memory\");\n            return;\n        }\n\n        *varsp = vars;\n    }\n\n    lua_pushnil(L);\n    while (lua_next(L, table) != 0) {\n\n        if (lua_type(L, -2) != LUA_TSTRING) {\n            luaL_error(L, \"attempt to use a non-string key in the \"\n                       \"\\\"vars\\\" option table\");\n            return;\n        }\n\n        if (!lua_isstring(L, -1)) {\n            luaL_error(L, \"attempt to use bad variable value type %s\",\n                       luaL_typename(L, -1));\n            return;\n        }\n\n        var = ngx_array_push(vars);\n        if (var == NULL) {\n            dd(\"here\");\n            luaL_error(L, \"no memory\");\n            return;\n        }\n\n        var->key.data = (u_char *) lua_tolstring(L, -2, &var->key.len);\n        var->value.data = (u_char *) lua_tolstring(L, -1, &var->value.len);\n\n        lua_pop(L, 1);\n    }\n}\n\n\nngx_int_t\nngx_http_lua_post_subrequest(ngx_http_request_t *r, void *data, ngx_int_t rc)\n{\n    ngx_http_request_t            *pr;\n    ngx_http_lua_ctx_t            *pr_ctx;\n    ngx_http_lua_ctx_t            *ctx; /* subrequest ctx */\n    ngx_http_lua_co_ctx_t         *pr_coctx;\n    size_t                         len;\n    ngx_str_t                     *body_str;\n    u_char                        *p;\n    ngx_chain_t                   *cl;\n\n    ngx_http_lua_post_subrequest_data_t    *psr_data = data;\n\n    ctx = psr_data->ctx;\n\n    if (ctx->run_post_subrequest) {\n        if (r != r->connection->data) {\n            r->connection->data = r;\n        }\n\n        return NGX_OK;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run post subrequest handler, rc:%i c:%ud\", rc,\n                   r->main->count);\n\n    ctx->run_post_subrequest = 1;\n\n    pr = r->parent;\n\n    pr_ctx = ngx_http_get_module_ctx(pr, ngx_http_lua_module);\n    if (pr_ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    pr_coctx = psr_data->pr_co_ctx;\n    pr_coctx->pending_subreqs--;\n\n    if (pr_coctx->pending_subreqs == 0) {\n        dd(\"all subrequests are done\");\n\n        pr_ctx->no_abort = 0;\n        pr_ctx->resume_handler = ngx_http_lua_subrequest_resume;\n        pr_ctx->cur_co_ctx = pr_coctx;\n    }\n\n    if (pr_ctx->entered_content_phase) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua restoring write event handler\");\n\n        pr->write_event_handler = ngx_http_lua_content_wev_handler;\n\n    } else {\n        pr->write_event_handler = ngx_http_core_run_phases;\n    }\n\n    dd(\"status rc = %d\", (int) rc);\n    dd(\"status headers_out.status = %d\", (int) r->headers_out.status);\n    dd(\"uri: %.*s\", (int) r->uri.len, r->uri.data);\n\n    /*  capture subrequest response status */\n\n    pr_coctx->sr_statuses[ctx->index] = r->headers_out.status;\n\n    if (pr_coctx->sr_statuses[ctx->index] == 0) {\n        if (rc == NGX_OK) {\n            rc = NGX_HTTP_OK;\n        }\n\n        if (rc == NGX_ERROR) {\n            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        if (rc >= 100) {\n            pr_coctx->sr_statuses[ctx->index] = rc;\n        }\n    }\n\n    if (!ctx->seen_last_for_subreq) {\n        pr_coctx->sr_flags[ctx->index] |= NGX_HTTP_LUA_SUBREQ_TRUNCATED;\n    }\n\n    dd(\"pr_coctx status: %d\", (int) pr_coctx->sr_statuses[ctx->index]);\n\n    /* copy subrequest response headers */\n    if (ctx->headers_set) {\n        rc = ngx_http_lua_set_content_type(r, ctx);\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"failed to set default content type: %i\", rc);\n            return NGX_ERROR;\n        }\n    }\n\n    pr_coctx->sr_headers[ctx->index] = &r->headers_out;\n\n    /* copy subrequest response body */\n\n    body_str = &pr_coctx->sr_bodies[ctx->index];\n\n    len = 0;\n    for (cl = ctx->body; cl; cl = cl->next) {\n        /*  ignore all non-memory buffers */\n        len += cl->buf->last - cl->buf->pos;\n    }\n\n    body_str->len = len;\n\n    if (len == 0) {\n        body_str->data = NULL;\n\n    } else {\n        p = ngx_palloc(r->pool, len);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        body_str->data = p;\n\n        /* copy from and then free the data buffers */\n\n        for (cl = ctx->body; cl; cl = cl->next) {\n            p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos);\n\n            cl->buf->last = cl->buf->pos;\n\n#if 0\n            dd(\"free body chain link buf ASAP\");\n            ngx_pfree(r->pool, cl->buf->start);\n#endif\n        }\n    }\n\n    if (ctx->body) {\n\n        ngx_chain_update_chains(r->pool,\n                                &pr_ctx->free_bufs, &pr_ctx->busy_bufs,\n                                &ctx->body,\n                                (ngx_buf_tag_t) &ngx_http_lua_module);\n\n        dd(\"free bufs: %p\", pr_ctx->free_bufs);\n    }\n\n    ngx_http_post_request_to_head(pr);\n\n    if (r != r->connection->data) {\n        r->connection->data = r;\n    }\n\n    if (rc == NGX_ERROR\n        || rc == NGX_HTTP_CREATED\n        || rc == NGX_HTTP_NO_CONTENT\n        || (rc >= NGX_HTTP_SPECIAL_RESPONSE\n            && rc != NGX_HTTP_CLOSE\n            && rc != NGX_HTTP_REQUEST_TIME_OUT\n            && rc != NGX_HTTP_CLIENT_CLOSED_REQUEST))\n    {\n        /* emulate ngx_http_special_response_handler */\n\n        if (rc > NGX_OK) {\n            r->err_status = rc;\n\n            r->expect_tested = 1;\n            r->headers_out.content_type.len = 0;\n            r->headers_out.content_length_n = 0;\n\n            ngx_http_clear_accept_ranges(r);\n            ngx_http_clear_last_modified(r);\n\n            rc = ngx_http_lua_send_header_if_needed(r, ctx);\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n        }\n\n        return NGX_OK;\n    }\n\n    return rc;\n}\n\n\nstatic void\nngx_http_lua_handle_subreq_responses(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx)\n{\n    ngx_uint_t                   i, count;\n    ngx_uint_t                   index;\n    lua_State                   *co;\n    ngx_str_t                   *body_str;\n    ngx_table_elt_t             *header;\n    ngx_list_part_t             *part;\n    ngx_http_headers_out_t      *sr_headers;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    u_char                  buf[sizeof(\"Mon, 28 Sep 1970 06:00:00 GMT\") - 1];\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua handle subrequest responses\");\n\n    coctx = ctx->cur_co_ctx;\n    co = coctx->co;\n\n    for (index = 0; index < coctx->nsubreqs; index++) {\n        dd(\"summary: reqs %d, subquery %d, pending %d, req %.*s\",\n           (int) coctx->nsubreqs,\n           (int) index,\n           (int) coctx->pending_subreqs,\n           (int) r->uri.len, r->uri.data);\n\n        /*  {{{ construct ret value */\n        lua_createtable(co, 0 /* narr */, 4 /* nrec */);\n\n        /*  copy captured status */\n        lua_pushinteger(co, coctx->sr_statuses[index]);\n        lua_setfield(co, -2, \"status\");\n\n        dd(\"captured subrequest flags: %d\", (int) coctx->sr_flags[index]);\n\n        /* set truncated flag if truncation happens */\n        if (coctx->sr_flags[index] & NGX_HTTP_LUA_SUBREQ_TRUNCATED) {\n            lua_pushboolean(co, 1);\n            lua_setfield(co, -2, \"truncated\");\n\n        } else {\n            lua_pushboolean(co, 0);\n            lua_setfield(co, -2, \"truncated\");\n        }\n\n        /*  copy captured body */\n\n        body_str = &coctx->sr_bodies[index];\n\n        lua_pushlstring(co, (char *) body_str->data, body_str->len);\n        lua_setfield(co, -2, \"body\");\n\n        if (body_str->data) {\n            dd(\"free body buffer ASAP\");\n            ngx_pfree(r->pool, body_str->data);\n        }\n\n        /* copy captured headers */\n\n        sr_headers = coctx->sr_headers[index];\n\n        part = &sr_headers->headers.part;\n        count = part->nelts;\n        while (part->next) {\n            part = part->next;\n            count += part->nelts;\n        }\n\n        lua_createtable(co, 0, count + 5); /* res.header */\n\n        dd(\"saving subrequest response headers\");\n\n        part = &sr_headers->headers.part;\n        header = part->elts;\n\n        for (i = 0; /* void */; i++) {\n\n            if (i >= part->nelts) {\n                if (part->next == NULL) {\n                    break;\n                }\n\n                part = part->next;\n                header = part->elts;\n                i = 0;\n            }\n\n            dd(\"checking sr header %.*s\", (int) header[i].key.len,\n               header[i].key.data);\n\n#if 1\n            if (header[i].hash == 0) {\n                continue;\n            }\n#endif\n\n            header[i].hash = 0;\n\n            dd(\"pushing sr header %.*s\", (int) header[i].key.len,\n               header[i].key.data);\n\n            lua_pushlstring(co, (char *) header[i].key.data,\n                            header[i].key.len); /* header key */\n            lua_pushvalue(co, -1); /* stack: table key key */\n\n            /* check if header already exists */\n            lua_rawget(co, -3); /* stack: table key value */\n\n            if (lua_isnil(co, -1)) {\n                lua_pop(co, 1); /* stack: table key */\n\n                lua_pushlstring(co, (char *) header[i].value.data,\n                                header[i].value.len);\n                    /* stack: table key value */\n\n                lua_rawset(co, -3); /* stack: table */\n\n            } else {\n\n                if (!lua_istable(co, -1)) { /* already inserted one value */\n                    lua_createtable(co, 4, 0);\n                        /* stack: table key value table */\n\n                    lua_insert(co, -2); /* stack: table key table value */\n                    lua_rawseti(co, -2, 1); /* stack: table key table */\n\n                    lua_pushlstring(co, (char *) header[i].value.data,\n                                    header[i].value.len);\n                        /* stack: table key table value */\n\n                    lua_rawseti(co, -2, lua_objlen(co, -2) + 1);\n                        /* stack: table key table */\n\n                    lua_rawset(co, -3); /* stack: table */\n\n                } else {\n                    lua_pushlstring(co, (char *) header[i].value.data,\n                                    header[i].value.len);\n                        /* stack: table key table value */\n\n                    lua_rawseti(co, -2, lua_objlen(co, -2) + 1);\n                        /* stack: table key table */\n\n                    lua_pop(co, 2); /* stack: table */\n                }\n            }\n        }\n\n        if (sr_headers->content_type.len) {\n            lua_pushliteral(co, \"Content-Type\"); /* header key */\n            lua_pushlstring(co, (char *) sr_headers->content_type.data,\n                            sr_headers->content_type.len); /* head key value */\n            lua_rawset(co, -3); /* head */\n        }\n\n        if (sr_headers->content_length == NULL\n            && sr_headers->content_length_n >= 0)\n        {\n            lua_pushliteral(co, \"Content-Length\"); /* header key */\n\n            lua_pushnumber(co, (lua_Number) sr_headers->content_length_n);\n                /* head key value */\n\n            lua_rawset(co, -3); /* head */\n        }\n\n        /* to work-around an issue in ngx_http_static_module\n         * (github issue #41) */\n        if (sr_headers->location && sr_headers->location->value.len) {\n            lua_pushliteral(co, \"Location\"); /* header key */\n            lua_pushlstring(co, (char *) sr_headers->location->value.data,\n                            sr_headers->location->value.len);\n            /* head key value */\n            lua_rawset(co, -3); /* head */\n        }\n\n        if (sr_headers->last_modified_time != -1) {\n            if (sr_headers->status != NGX_HTTP_OK\n                && sr_headers->status != NGX_HTTP_PARTIAL_CONTENT\n                && sr_headers->status != NGX_HTTP_NOT_MODIFIED\n                && sr_headers->status != NGX_HTTP_NO_CONTENT)\n            {\n                sr_headers->last_modified_time = -1;\n                sr_headers->last_modified = NULL;\n            }\n        }\n\n        if (sr_headers->last_modified == NULL\n            && sr_headers->last_modified_time != -1)\n        {\n            (void) ngx_http_time(buf, sr_headers->last_modified_time);\n\n            lua_pushliteral(co, \"Last-Modified\"); /* header key */\n            lua_pushlstring(co, (char *) buf, sizeof(buf)); /* head key value */\n            lua_rawset(co, -3); /* head */\n        }\n\n        lua_setfield(co, -2, \"header\");\n\n        /*  }}} */\n    }\n}\n\n\nvoid\nngx_http_lua_inject_subrequest_api(lua_State *L)\n{\n    lua_createtable(L, 0 /* narr */, 2 /* nrec */); /* .location */\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_location_capture);\n    lua_setfield(L, -2, \"capture\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_location_capture_multi);\n    lua_setfield(L, -2, \"capture_multi\");\n\n    lua_setfield(L, -2, \"location\");\n}\n\n\nstatic ngx_int_t\nngx_http_lua_subrequest(ngx_http_request_t *r,\n    ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,\n    ngx_http_post_subrequest_t *ps, ngx_uint_t flags)\n{\n#if !defined freenginx\n    ngx_time_t                    *tp;\n#endif\n    ngx_connection_t              *c;\n    ngx_http_request_t            *sr;\n    ngx_http_core_srv_conf_t      *cscf;\n\n#if (nginx_version >= 1009005)\n\n    if (r->subrequests == 0) {\n#if defined(NGX_DTRACE) && NGX_DTRACE\n        ngx_http_probe_subrequest_cycle(r, uri, args);\n#endif\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"lua subrequests cycle while processing \\\"%V\\\"\", uri);\n        return NGX_ERROR;\n    }\n\n#else  /* nginx_version <= 1009004 */\n\n    r->main->subrequests--;\n\n    if (r->main->subrequests == 0) {\n#if defined(NGX_DTRACE) && NGX_DTRACE\n        ngx_http_probe_subrequest_cycle(r, uri, args);\n#endif\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"lua subrequests cycle while processing \\\"%V\\\"\", uri);\n        r->main->subrequests = 1;\n        return NGX_ERROR;\n    }\n\n#endif\n\n    sr = ngx_pcalloc(r->pool, sizeof(ngx_http_request_t));\n    if (sr == NULL) {\n        return NGX_ERROR;\n    }\n\n    sr->signature = NGX_HTTP_MODULE;\n\n    c = r->connection;\n    sr->connection = c;\n\n    sr->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);\n    if (sr->ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_list_init(&sr->headers_out.headers, r->pool, 20,\n                      sizeof(ngx_table_elt_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n    sr->main_conf = cscf->ctx->main_conf;\n    sr->srv_conf = cscf->ctx->srv_conf;\n    sr->loc_conf = cscf->ctx->loc_conf;\n\n    sr->pool = r->pool;\n\n    sr->headers_in.content_length_n = -1;\n    sr->headers_in.keep_alive_n = -1;\n\n    ngx_http_clear_content_length(sr);\n    ngx_http_clear_accept_ranges(sr);\n    ngx_http_clear_last_modified(sr);\n\n    sr->request_body = r->request_body;\n\n#if (NGX_HTTP_SPDY)\n    sr->spdy_stream = r->spdy_stream;\n#endif\n\n#if (NGX_HTTP_V2)\n    sr->stream = r->stream;\n#endif\n\n#ifdef HAVE_ALLOW_REQUEST_BODY_UPDATING_PATCH\n    sr->content_length_n = -1;\n#endif\n\n    sr->method = NGX_HTTP_GET;\n    sr->http_version = r->http_version;\n\n    sr->request_line = r->request_line;\n    sr->uri = *uri;\n\n    if (args) {\n        sr->args = *args;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"lua http subrequest \\\"%V?%V\\\"\", uri, &sr->args);\n\n    sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0;\n    sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0;\n\n    sr->unparsed_uri = r->unparsed_uri;\n    sr->method_name = ngx_http_core_get_method;\n    sr->http_protocol = r->http_protocol;\n\n    ngx_http_set_exten(sr);\n\n    sr->main = r->main;\n    sr->parent = r;\n    sr->post_subrequest = ps;\n    sr->read_event_handler = ngx_http_request_empty_handler;\n    sr->write_event_handler = ngx_http_handler;\n\n    sr->variables = r->variables;\n\n    sr->log_handler = r->log_handler;\n\n    sr->internal = 1;\n\n    sr->discard_body = r->discard_body;\n    sr->expect_tested = 1;\n    sr->main_filter_need_in_memory = r->main_filter_need_in_memory;\n\n    sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;\n\n#if (nginx_version >= 1009005)\n    sr->subrequests = r->subrequests - 1;\n#endif\n\n#if (defined freenginx && nginx_version >= 1029000)\n    sr->start_time = ngx_current_msec;\n#else\n    tp = ngx_timeofday();\n    sr->start_sec = tp->sec;\n    sr->start_msec = tp->msec;\n#endif\n\n    r->main->count++;\n\n    *psr = sr;\n\n#if defined(NGX_DTRACE) && NGX_DTRACE\n    ngx_http_probe_subrequest_start(sr);\n#endif\n\n    return ngx_http_post_request(sr, NULL);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_subrequest_resume(ngx_http_request_t *r)\n{\n    lua_State                   *vm;\n    ngx_int_t                    rc;\n    ngx_uint_t                   nreqs;\n    ngx_connection_t            *c;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->resume_handler = ngx_http_lua_wev_handler;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run subrequests done, resuming lua thread\");\n\n    coctx = ctx->cur_co_ctx;\n\n    dd(\"nsubreqs: %d\", (int) coctx->nsubreqs);\n\n    ngx_http_lua_handle_subreq_responses(r, ctx);\n\n    dd(\"free sr_statues/headers/bodies memory ASAP\");\n\n#if 1\n    ngx_pfree(r->pool, coctx->sr_statuses);\n\n    coctx->sr_statuses = NULL;\n    coctx->sr_headers = NULL;\n    coctx->sr_bodies = NULL;\n    coctx->sr_flags = NULL;\n#endif\n\n    c = r->connection;\n    vm = ngx_http_lua_get_lua_vm(r, ctx);\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(vm, r, ctx, coctx->nsubreqs);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run thread returned %d\", rc);\n\n    if (rc == NGX_AGAIN) {\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    /* rc == NGX_ERROR || rc >= NGX_OK */\n\n    if (ctx->entered_content_phase) {\n        ngx_http_lua_finalize_request(r, rc);\n        return NGX_DONE;\n    }\n\n    return rc;\n}\n\n\nstatic void\nngx_http_lua_cancel_subreq(ngx_http_request_t *r)\n{\n    ngx_http_posted_request_t   *pr;\n    ngx_http_posted_request_t  **p;\n\n#if 1\n    r->main->count--;\n    r->main->subrequests++;\n#endif\n\n    p = &r->main->posted_requests;\n    for (pr = r->main->posted_requests; pr->next; pr = pr->next) {\n        p = &pr->next;\n    }\n\n    *p = NULL;\n\n    r->connection->data = r->parent;\n}\n\n\nstatic ngx_int_t\nngx_http_post_request_to_head(ngx_http_request_t *r)\n{\n    ngx_http_posted_request_t  *pr;\n\n    pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));\n    if (pr == NULL) {\n        return NGX_ERROR;\n    }\n\n    pr->request = r;\n    pr->next = r->main->posted_requests;\n    r->main->posted_requests = pr;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_copy_in_file_request_body(ngx_http_request_t *r)\n{\n    ngx_temp_file_t     *tf;\n\n    ngx_http_request_body_t   *body;\n\n    tf = r->request_body->temp_file;\n\n    if (!tf->persistent || !tf->clean) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"the request body was not read by ngx_lua\");\n\n        return NGX_ERROR;\n    }\n\n    body = ngx_palloc(r->pool, sizeof(ngx_http_request_body_t));\n    if (body == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(body, r->request_body, sizeof(ngx_http_request_body_t));\n\n    body->temp_file = ngx_palloc(r->pool, sizeof(ngx_temp_file_t));\n    if (body->temp_file == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(body->temp_file, tf, sizeof(ngx_temp_file_t));\n    dd(\"file fd: %d\", body->temp_file->file.fd);\n\n    r->request_body = body;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_copy_request_headers(ngx_http_request_t *sr,\n    ngx_http_request_t *pr, int pr_not_chunked, ngx_array_t *extra_headers)\n{\n    ngx_table_elt_t                 *clh, *header;\n    ngx_list_part_t                 *part;\n    ngx_keyval_t                    *header_keyval;\n    ngx_chain_t                     *in;\n    ngx_uint_t                       i;\n    u_char                          *p;\n    off_t                            len;\n\n    dd(\"before: parent req headers count: %d\",\n       (int) pr->headers_in.headers.part.nelts);\n\n    if (ngx_list_init(&sr->headers_in.headers, sr->pool, 20,\n                      sizeof(ngx_table_elt_t)) != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (sr->request_body && !pr_not_chunked) {\n\n        /* craft our own Content-Length */\n        len = 0;\n\n        for (in = sr->request_body->bufs; in; in = in->next) {\n            len += ngx_buf_size(in->buf);\n        }\n\n        clh = ngx_list_push(&sr->headers_in.headers);\n        if (clh == NULL) {\n            return NGX_ERROR;\n        }\n\n        clh->hash = ngx_http_lua_content_length_hash;\n        clh->key = ngx_http_lua_content_length_header_key;\n#if defined(nginx_version) && nginx_version >= 1023000\n        clh->next = NULL;\n#endif\n        clh->lowcase_key = ngx_pnalloc(sr->pool, clh->key.len);\n        if (clh->lowcase_key == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_strlow(clh->lowcase_key, clh->key.data, clh->key.len);\n\n        p = ngx_palloc(sr->pool, NGX_OFF_T_LEN);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        clh->value.data = p;\n        clh->value.len = ngx_sprintf(clh->value.data, \"%O\", len)\n                         - clh->value.data;\n\n        sr->headers_in.content_length = clh;\n        sr->headers_in.content_length_n = len;\n\n        dd(\"sr crafted content-length: %.*s\",\n           (int) sr->headers_in.content_length->value.len,\n           sr->headers_in.content_length->value.data);\n    }\n\n    /* copy the parent request's headers */\n\n    part = &pr->headers_in.headers.part;\n    header = part->elts;\n\n#if (NGX_HTTP_V3)\n    if (pr->headers_in.server.data != NULL) {\n        if (ngx_http_lua_set_input_header(sr, host_header,\n                                          pr->headers_in.server, 0)\n            == NGX_ERROR)\n        {\n            return NGX_ERROR;\n        }\n    }\n#endif\n\n    for (i = 0; /* void */; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n\n            part = part->next;\n            header = part->elts;\n            i = 0;\n        }\n\n        if (!pr_not_chunked && header[i].key.len == sizeof(\"Content-Length\") - 1\n            && ngx_strncasecmp(header[i].key.data, (u_char *) \"Content-Length\",\n                               sizeof(\"Content-Length\") - 1) == 0)\n        {\n            continue;\n        }\n\n        dd(\"sr copied req header %.*s: %.*s\", (int) header[i].key.len,\n           header[i].key.data, (int) header[i].value.len,\n           header[i].value.data);\n\n        if (ngx_http_lua_set_input_header(sr, header[i].key,\n                                          header[i].value, 0) == NGX_ERROR)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    if (extra_headers && extra_headers->nelts > 0) {\n\n        header_keyval = extra_headers->elts;\n\n        for (i = 0; i < extra_headers->nelts; i++, header_keyval++) {\n\n            if (ngx_http_lua_set_input_header(sr, header_keyval->key,\n                                        header_keyval->value, 1) == NGX_ERROR)\n            {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    dd(\"after: parent req headers count: %d\",\n       (int) pr->headers_in.headers.part.nelts);\n\n    return NGX_OK;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_subrequest.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_SUBREQUEST_H_INCLUDED_\n#define _NGX_HTTP_LUA_SUBREQUEST_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_subrequest_api(lua_State *L);\nngx_int_t ngx_http_lua_post_subrequest(ngx_http_request_t *r, void *data,\n    ngx_int_t rc);\n\n\nextern ngx_str_t  ngx_http_lua_get_method;\nextern ngx_str_t  ngx_http_lua_put_method;\nextern ngx_str_t  ngx_http_lua_post_method;\nextern ngx_str_t  ngx_http_lua_head_method;\nextern ngx_str_t  ngx_http_lua_delete_method;\nextern ngx_str_t  ngx_http_lua_options_method;\nextern ngx_str_t  ngx_http_lua_copy_method;\nextern ngx_str_t  ngx_http_lua_move_method;\nextern ngx_str_t  ngx_http_lua_lock_method;\nextern ngx_str_t  ngx_http_lua_mkcol_method;\nextern ngx_str_t  ngx_http_lua_propfind_method;\nextern ngx_str_t  ngx_http_lua_proppatch_method;\nextern ngx_str_t  ngx_http_lua_unlock_method;\nextern ngx_str_t  ngx_http_lua_patch_method;\nextern ngx_str_t  ngx_http_lua_trace_method;\n\n\ntypedef struct ngx_http_lua_post_subrequest_data_s {\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *pr_co_ctx;\n\n} ngx_http_lua_post_subrequest_data_t;\n\n\n#endif /* _NGX_HTTP_LUA_SUBREQUEST_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_time.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_common.h\"\n\n\ndouble\nngx_http_lua_ffi_now(void)\n{\n    ngx_time_t              *tp;\n\n    tp = ngx_timeofday();\n\n    return tp->sec + tp->msec / 1000.0;\n}\n\n\ndouble\nngx_http_lua_ffi_req_start_time(ngx_http_request_t *r)\n{\n#if (defined freenginx && nginx_version >= 1029000)\n    ngx_time_t              *tp;\n\n    tp = ngx_timeofday();\n    tp->sec -= (ngx_current_msec - r->start_time) / 1000;\n    tp->msec -= (ngx_current_msec - r->start_time) % 1000;\n    if (tp->msec > NGX_MAX_INT_T_VALUE) {\n        tp->msec += 1000;\n        tp->sec -= 1;\n    }\n\n    return tp->sec + tp->msec / 1000.0;\n#else\n    return r->start_sec + r->start_msec / 1000.0;\n#endif\n}\n\n\nlong\nngx_http_lua_ffi_time(void)\n{\n    return (long) ngx_time();\n}\n\n\nlong\nngx_http_lua_ffi_monotonic_msec(void)\n{\n    return (long) ngx_current_msec;\n}\n\n\nvoid\nngx_http_lua_ffi_update_time(void)\n{\n    ngx_time_update();\n}\n\n\nvoid\nngx_http_lua_ffi_today(u_char *buf)\n{\n    ngx_tm_t                 tm;\n\n    ngx_gmtime(ngx_time() + ngx_cached_time->gmtoff * 60, &tm);\n\n    ngx_sprintf(buf, \"%04d-%02d-%02d\", tm.ngx_tm_year, tm.ngx_tm_mon,\n                tm.ngx_tm_mday);\n}\n\n\nvoid\nngx_http_lua_ffi_localtime(u_char *buf)\n{\n    ngx_tm_t                 tm;\n\n    ngx_gmtime(ngx_time() + ngx_cached_time->gmtoff * 60, &tm);\n\n    ngx_sprintf(buf, \"%04d-%02d-%02d %02d:%02d:%02d\", tm.ngx_tm_year,\n                tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min,\n                tm.ngx_tm_sec);\n}\n\n\nvoid\nngx_http_lua_ffi_utctime(u_char *buf)\n{\n    ngx_tm_t       tm;\n\n    ngx_gmtime(ngx_time(), &tm);\n\n    ngx_sprintf(buf, \"%04d-%02d-%02d %02d:%02d:%02d\", tm.ngx_tm_year,\n                tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min,\n                tm.ngx_tm_sec);\n}\n\n\nint\nngx_http_lua_ffi_cookie_time(u_char *buf, long t)\n{\n    u_char                              *p;\n\n    p = ngx_http_cookie_time(buf, t);\n    return p - buf;\n}\n\n\nvoid\nngx_http_lua_ffi_http_time(u_char *buf, long t)\n{\n    ngx_http_time(buf, t);\n}\n\n\nvoid\nngx_http_lua_ffi_parse_http_time(const u_char *str, size_t len,\n    long *time)\n{\n    /* ngx_http_parse_time doesn't modify 'str' actually */\n    *time = ngx_http_parse_time((u_char *) str, len);\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_timer.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_timer.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_probe.h\"\n\n\n#define NGX_HTTP_LUA_TIMER_ERRBUF_SIZE  128\n\n\ntypedef struct {\n    void        **main_conf;\n    void        **srv_conf;\n    void        **loc_conf;\n\n    lua_State    *co;\n\n    ngx_pool_t   *pool;\n\n    ngx_listening_t                   *listening;\n    ngx_str_t                          client_addr_text;\n\n    ngx_http_lua_main_conf_t          *lmcf;\n    ngx_http_lua_vm_state_t           *vm_state;\n\n    int           co_ref;\n    unsigned      delay:31;\n    unsigned      premature:1;\n} ngx_http_lua_timer_ctx_t;\n\n\nstatic int ngx_http_lua_ngx_timer_at(lua_State *L);\nstatic int ngx_http_lua_ngx_timer_every(lua_State *L);\nstatic int ngx_http_lua_ngx_timer_helper(lua_State *L, int every);\nstatic int ngx_http_lua_ngx_timer_running_count(lua_State *L);\nstatic int ngx_http_lua_ngx_timer_pending_count(lua_State *L);\nstatic ngx_int_t ngx_http_lua_timer_copy(ngx_http_lua_timer_ctx_t *old_tctx);\nstatic void ngx_http_lua_timer_handler(ngx_event_t *ev);\nstatic u_char *ngx_http_lua_log_timer_error(ngx_log_t *log, u_char *buf,\n    size_t len);\nstatic void ngx_http_lua_abort_pending_timers(ngx_event_t *ev);\n\n\nvoid\nngx_http_lua_inject_timer_api(lua_State *L)\n{\n    lua_createtable(L, 0 /* narr */, 4 /* nrec */);    /* ngx.timer. */\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_timer_at);\n    lua_setfield(L, -2, \"at\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_timer_every);\n    lua_setfield(L, -2, \"every\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_timer_running_count);\n    lua_setfield(L, -2, \"running_count\");\n\n    lua_pushcfunction(L, ngx_http_lua_ngx_timer_pending_count);\n    lua_setfield(L, -2, \"pending_count\");\n\n    lua_setfield(L, -2, \"timer\");\n}\n\n\nstatic int\nngx_http_lua_ngx_timer_running_count(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request\");\n    }\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    lua_pushnumber(L, lmcf->running_timers);\n\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_ngx_timer_pending_count(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request\");\n    }\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    lua_pushnumber(L, lmcf->pending_timers);\n\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_ngx_timer_at(lua_State *L)\n{\n    return ngx_http_lua_ngx_timer_helper(L, 0);\n}\n\n\n/*\n * TODO: return a timer handler instead which can be passed to\n * the ngx.timer.cancel method to cancel the timer.\n */\nstatic int\nngx_http_lua_ngx_timer_every(lua_State *L)\n{\n    return ngx_http_lua_ngx_timer_helper(L, 1);\n}\n\n\nstatic int\nngx_http_lua_ngx_timer_helper(lua_State *L, int every)\n{\n    int                      nargs;\n    int                      co_ref;\n    u_char                  *p;\n    lua_State               *vm;  /* the main thread */\n    lua_State               *co;\n    ngx_msec_t               delay;\n    ngx_event_t             *ev = NULL;\n    ngx_http_request_t      *r;\n    ngx_connection_t        *saved_c = NULL;\n    ngx_http_lua_ctx_t      *ctx;\n#if 0\n    ngx_http_connection_t   *hc;\n#endif\n\n    ngx_http_lua_timer_ctx_t      *tctx = NULL;\n    ngx_http_lua_main_conf_t      *lmcf;\n#if 0\n    ngx_http_core_main_conf_t     *cmcf;\n#endif\n\n    nargs = lua_gettop(L);\n    if (nargs < 2) {\n        return luaL_error(L, \"expecting at least 2 arguments but got %d\",\n                          nargs);\n    }\n\n    delay = (ngx_msec_t) (luaL_checknumber(L, 1) * 1000);\n\n    if (every && delay == 0) {\n        return luaL_error(L, \"delay cannot be zero\");\n    }\n\n    luaL_argcheck(L, lua_isfunction(L, 2) && !lua_iscfunction(L, 2), 2,\n                  \"Lua function expected\");\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    ngx_http_lua_assert(ctx != NULL);\n\n    /*\n     * Since nginx has been confirmed that all timers have been cleaned up when\n     * exit worker is executed, all timers will no longer be executed in exit\n     * worker phase.\n     * Reference https://github.com/nginx/nginx/blob/f02e2a734ef472f0dcf83ab2\n     * e8ce96d1acead8a5/src/os/unix/ngx_process_cycle.c#L715\n     */\n    ngx_http_lua_check_context(L, ctx, ~NGX_HTTP_LUA_CONTEXT_EXIT_WORKER);\n\n    if (ngx_exiting && delay > 0) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"process exiting\");\n        return 2;\n    }\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    if (lmcf->pending_timers >= lmcf->max_pending_timers) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"too many pending timers\");\n        return 2;\n    }\n\n    if (lmcf->watcher == NULL) {\n        /* create the watcher fake connection */\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua creating fake watcher connection\");\n\n        if (ngx_cycle->files) {\n            saved_c = ngx_cycle->files[0];\n        }\n\n        lmcf->watcher = ngx_get_connection(0, ngx_cycle->log);\n\n        if (ngx_cycle->files) {\n            ngx_cycle->files[0] = saved_c;\n        }\n\n        if (lmcf->watcher == NULL) {\n            return luaL_error(L, \"no memory\");\n        }\n\n        /* to work around the -1 check in ngx_worker_process_cycle: */\n        lmcf->watcher->fd = (ngx_socket_t) -2;\n\n        lmcf->watcher->idle = 1;\n        lmcf->watcher->read->handler = ngx_http_lua_abort_pending_timers;\n        lmcf->watcher->data = lmcf;\n    }\n\n    vm = ngx_http_lua_get_lua_vm(r, ctx);\n\n    co_ref = ngx_http_lua_new_cached_thread(vm, &co, lmcf, 1);\n\n    /* vm stack: coroutines thread */\n\n    /* L stack: time func [args] */\n\n    ngx_http_lua_probe_user_coroutine_create(r, L, co);\n\n    /* co stack: <empty> */\n\n    dd(\"stack top: %d\", lua_gettop(L));\n\n    lua_xmove(vm, L, 1);    /* move coroutine from main thread to L */\n\n    lua_pop(vm, 1);  /* pop coroutines */\n\n    /* L stack: time func [args] thread */\n    /* vm stack: empty */\n\n    lua_pushvalue(L, 2);    /* copy entry function to top of L*/\n\n    /* L stack: time func [args] thread func */\n\n    lua_xmove(L, co, 1);    /* move entry function from L to co */\n\n    /* L stack: time func [args] thread */\n    /* co stack: func */\n\n#ifndef OPENRESTY_LUAJIT\n    ngx_http_lua_get_globals_table(co);\n    lua_setfenv(co, -2);\n#endif\n\n    /* co stack: func */\n\n    /* L stack: time func [args] thread */\n\n    if (nargs > 2) {\n        lua_pop(L, 1);  /* L stack: time func [args] */\n        lua_xmove(L, co, nargs - 2);  /* L stack: time func */\n\n        /* co stack: func [args] */\n    }\n\n    p = ngx_alloc(sizeof(ngx_event_t) + sizeof(ngx_http_lua_timer_ctx_t),\n                  r->connection->log);\n    if (p == NULL) {\n        goto nomem;\n    }\n\n    ev = (ngx_event_t *) p;\n\n    ngx_memzero(ev, sizeof(ngx_event_t));\n\n    p += sizeof(ngx_event_t);\n\n    tctx = (ngx_http_lua_timer_ctx_t *) p;\n\n    tctx->delay = every ? delay : 0;\n\n    tctx->premature = 0;\n    tctx->co_ref = co_ref;\n    tctx->co = co;\n    tctx->main_conf = r->main_conf;\n    tctx->srv_conf = r->srv_conf;\n    tctx->loc_conf = r->loc_conf;\n    tctx->lmcf = lmcf;\n\n    tctx->pool = ngx_create_pool(128, ngx_cycle->log);\n    if (tctx->pool == NULL) {\n        goto nomem;\n    }\n\n    if (r->connection) {\n        tctx->listening = r->connection->listening;\n\n    } else {\n        tctx->listening = NULL;\n    }\n\n    if (r->connection->addr_text.len) {\n        tctx->client_addr_text.data = ngx_palloc(tctx->pool,\n                                                 r->connection->addr_text.len);\n        if (tctx->client_addr_text.data == NULL) {\n            goto nomem;\n        }\n\n        ngx_memcpy(tctx->client_addr_text.data, r->connection->addr_text.data,\n                   r->connection->addr_text.len);\n        tctx->client_addr_text.len = r->connection->addr_text.len;\n\n    } else {\n        tctx->client_addr_text.len = 0;\n        tctx->client_addr_text.data = NULL;\n    }\n\n    if (ctx->vm_state) {\n        tctx->vm_state = ctx->vm_state;\n        tctx->vm_state->count++;\n\n    } else {\n        tctx->vm_state = NULL;\n    }\n\n    ev->handler = ngx_http_lua_timer_handler;\n    ev->data = tctx;\n    ev->log = ngx_cycle->log;\n\n    lmcf->pending_timers++;\n\n#ifdef HAVE_POSTED_DELAYED_EVENTS_PATCH\n    if (delay == 0 && !ngx_exiting) {\n        dd(\"posting 0 sec sleep event to head of delayed queue\");\n        ngx_post_event(ev, &ngx_posted_delayed_events);\n\n        lua_pushinteger(L, 1);\n        return 1;\n    }\n#endif\n\n    ngx_add_timer(ev, delay);\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"created timer (co: %p delay: %M ms): sz=%d\", tctx->co,\n                   delay, lua_gettop(L));\n\n    lua_pushinteger(L, 1);\n    return 1;\n\nnomem:\n\n    if (tctx && tctx->pool) {\n        ngx_destroy_pool(tctx->pool);\n    }\n\n    if (ev) {\n        ngx_free(ev);\n    }\n\n    ngx_http_lua_free_thread(r, L, co_ref, co, lmcf);\n\n    return luaL_error(L, \"no memory\");\n}\n\n\nstatic ngx_int_t\nngx_http_lua_timer_copy(ngx_http_lua_timer_ctx_t *old_tctx)\n{\n    int                          nargs, co_ref, i;\n    u_char                      *p;\n    lua_State                   *vm;  /* the main thread */\n    lua_State                   *co;\n    lua_State                   *L;\n    ngx_event_t                 *ev = NULL;\n    ngx_http_lua_timer_ctx_t    *tctx = NULL;\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    /* L stack: func [args] */\n    L = old_tctx->co;\n\n    lmcf = old_tctx->lmcf;\n\n    vm = old_tctx->vm_state ? old_tctx->vm_state->vm : lmcf->lua;\n\n    co_ref = ngx_http_lua_new_cached_thread(vm, &co, lmcf, 1);\n\n    /* co stack: <empty> */\n\n    dd(\"stack top: %d\", lua_gettop(L));\n\n    lua_xmove(vm, L, 1);    /* move coroutine from main thread to L */\n\n    lua_pop(vm, 1);  /* pop coroutines */\n\n    /* L stack: func [args] thread */\n    /* vm stack: empty */\n\n    lua_pushvalue(L, 1);    /* copy entry function to top of L*/\n\n    /* L stack: func [args] thread func */\n\n    lua_xmove(L, co, 1);    /* move entry function from L to co */\n\n    /* L stack: func [args] thread */\n    /* co stack: func */\n\n#ifndef OPENRESTY_LUAJIT\n    ngx_http_lua_get_globals_table(co);\n    lua_setfenv(co, -2);\n#endif\n\n    /* co stack: func */\n\n    lua_pop(L, 1);  /* pop thread */\n\n    /* L stack: func [args] */\n\n    nargs = lua_gettop(L);\n    if (nargs > 1) {\n        for (i = 2; i <= nargs; i++) {\n            lua_pushvalue(L, i);\n        }\n\n        /* L stack: func [args] [args] */\n\n        lua_xmove(L, co, nargs - 1);\n\n        /* L stack: func [args] */\n        /* co stack: func [args] */\n    }\n\n    p = ngx_alloc(sizeof(ngx_event_t) + sizeof(ngx_http_lua_timer_ctx_t),\n                  ngx_cycle->log);\n    if (p == NULL) {\n        goto nomem;\n    }\n\n    ev = (ngx_event_t *) p;\n\n    ngx_memzero(ev, sizeof(ngx_event_t));\n\n    p += sizeof(ngx_event_t);\n\n    tctx = (ngx_http_lua_timer_ctx_t *) p;\n\n    ngx_memcpy(tctx, old_tctx, sizeof(ngx_http_lua_timer_ctx_t));\n\n    tctx->co_ref = co_ref;\n    tctx->co = co;\n\n    tctx->pool = ngx_create_pool(128, ngx_cycle->log);\n    if (tctx->pool == NULL) {\n        goto nomem;\n    }\n\n    if (tctx->client_addr_text.len) {\n        tctx->client_addr_text.data = ngx_palloc(tctx->pool,\n                                                 tctx->client_addr_text.len);\n        if (tctx->client_addr_text.data == NULL) {\n            goto nomem;\n        }\n\n        ngx_memcpy(tctx->client_addr_text.data, old_tctx->client_addr_text.data,\n                   tctx->client_addr_text.len);\n    }\n\n    if (tctx->vm_state) {\n        tctx->vm_state->count++;\n    }\n\n    ev->handler = ngx_http_lua_timer_handler;\n    ev->data = tctx;\n    ev->log = ngx_cycle->log;\n\n    lmcf->pending_timers++;\n\n    ngx_add_timer(ev, tctx->delay);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"created next timer (co: %p delay: %M ms)\", tctx->co,\n                   tctx->delay);\n\n    return NGX_OK;\n\nnomem:\n\n    if (tctx && tctx->pool) {\n        ngx_destroy_pool(tctx->pool);\n    }\n\n    if (ev) {\n        ngx_free(ev);\n    }\n\n    /* L stack: func [args] */\n\n    ngx_http_lua_free_thread(NULL, L, co_ref, co, lmcf);\n\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_http_lua_timer_handler(ngx_event_t *ev)\n{\n    int                      n;\n    lua_State               *L = NULL;\n    ngx_int_t                rc;\n    ngx_connection_t        *c = NULL;\n    ngx_http_request_t      *r = NULL;\n    ngx_http_lua_ctx_t      *ctx;\n    ngx_pool_cleanup_t      *cln;\n    ngx_pool_cleanup_t      *pcln;\n\n    ngx_http_lua_timer_ctx_t         tctx;\n    ngx_http_lua_main_conf_t        *lmcf;\n    ngx_http_core_loc_conf_t        *clcf;\n\n    lua_Debug                ar;\n    u_char                  *p;\n    u_char                   errbuf[NGX_HTTP_LUA_TIMER_ERRBUF_SIZE];\n    const char              *source;\n    const char              *errmsg;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua ngx.timer expired\");\n\n    ngx_memcpy(&tctx, ev->data, sizeof(ngx_http_lua_timer_ctx_t));\n    ngx_free(ev);\n\n    ngx_http_lua_assert(tctx.co_ref && tctx.co);\n\n    lmcf = tctx.lmcf;\n\n    lmcf->pending_timers--;\n\n    if (!ngx_exiting && tctx.delay > 0) {\n        rc = ngx_http_lua_timer_copy(&tctx);\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                          \"failed to create the next timer of delay %ud ms\",\n                          (unsigned) tctx.delay);\n        }\n    }\n\n    if (lmcf->running_timers >= lmcf->max_running_timers) {\n        p = ngx_snprintf(errbuf, NGX_HTTP_LUA_TIMER_ERRBUF_SIZE - 1,\n                         \"%i lua_max_running_timers are not enough\",\n                         lmcf->max_running_timers);\n        *p = '\\0';\n        errmsg = (const char *) errbuf;\n        goto failed;\n    }\n\n    c = ngx_http_lua_create_fake_connection(tctx.pool);\n    if (c == NULL) {\n        errmsg = \"could not create fake connection\";\n        /* tctx.pool is freed in ngx_http_lua_create_fake_connection */\n        tctx.pool = NULL;\n        goto failed;\n    }\n\n    c->log->handler = ngx_http_lua_log_timer_error;\n    c->log->data = c;\n\n    c->listening = tctx.listening;\n    c->addr_text = tctx.client_addr_text;\n\n    r = ngx_http_lua_create_fake_request(c);\n    if (r == NULL) {\n        errmsg = \"could not create fake request\";\n        goto failed;\n    }\n\n    r->main_conf = tctx.main_conf;\n    r->srv_conf = tctx.srv_conf;\n    r->loc_conf = tctx.loc_conf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n#if (nginx_version >= 1009000)\n    ngx_set_connection_log(r->connection, clcf->error_log);\n\n#else\n    ngx_http_set_connection_log(r->connection, clcf->error_log);\n#endif\n\n    dd(\"lmcf: %p\", lmcf);\n\n    ctx = ngx_http_lua_create_ctx(r);\n    if (ctx == NULL) {\n        errmsg = \"could not create ctx\";\n        goto failed;\n    }\n\n    if (tctx.vm_state) {\n        ctx->vm_state = tctx.vm_state;\n\n        pcln = ngx_pool_cleanup_add(r->pool, 0);\n        if (pcln == NULL) {\n            errmsg = \"could not add vm cleanup\";\n            goto failed;\n        }\n\n        pcln->handler = ngx_http_lua_cleanup_vm;\n        pcln->data = tctx.vm_state;\n    }\n\n    ctx->cur_co_ctx = &ctx->entry_co_ctx;\n\n    L = ngx_http_lua_get_lua_vm(r, ctx);\n\n    cln = ngx_pool_cleanup_add(r->pool, 0);\n    if (cln == NULL) {\n        errmsg = \"could not add request cleanup\";\n        goto failed;\n    }\n\n    cln->handler = ngx_http_lua_request_cleanup_handler;\n    cln->data = ctx;\n    ctx->cleanup = &cln->handler;\n\n    ctx->entered_content_phase = 1;\n    ctx->context = NGX_HTTP_LUA_CONTEXT_TIMER;\n\n    r->read_event_handler = ngx_http_block_reading;\n\n    ctx->cur_co_ctx->co_ref = tctx.co_ref;\n    ctx->cur_co_ctx->co = tctx.co;\n    ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING;\n\n    dd(\"r connection: %p, log %p\", r->connection, r->connection->log);\n\n    /*  save the request in coroutine globals table */\n    ngx_http_lua_set_req(tctx.co, r);\n\n    ngx_http_lua_attach_co_ctx_to_L(tctx.co, ctx->cur_co_ctx);\n\n    lmcf->running_timers++;\n\n    lua_pushboolean(tctx.co, tctx.premature);\n\n    n = lua_gettop(tctx.co);\n    if (n > 2) {\n        lua_insert(tctx.co, 2);\n    }\n\n#ifdef NGX_LUA_USE_ASSERT\n    ctx->cur_co_ctx->co_top = 1;\n#endif\n\n    rc = ngx_http_lua_run_thread(L, r, ctx, n - 1);\n\n    dd(\"timer lua run thread: %d\", (int) rc);\n\n    if (rc == NGX_ERROR || rc >= NGX_OK) {\n        /* do nothing */\n\n    } else if (rc == NGX_AGAIN) {\n        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0);\n\n    } else if (rc == NGX_DONE) {\n        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 1);\n\n    } else {\n        rc = NGX_OK;\n    }\n\n    ngx_http_lua_finalize_request(r, rc);\n    return;\n\nfailed:\n\n    /* co stack: func [args] */\n    lua_pushvalue(tctx.co, 1);\n    /* co stack: func [args] func */\n    lua_getinfo(tctx.co, \">Sf\", &ar);\n\n    source = ar.source;\n\n    if (source == NULL) {\n        source = \"(unknown)\";\n    }\n\n    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                  \"lua failed to run timer with function defined at %s:%d: %s\",\n                  source, ar.linedefined, errmsg);\n\n#if 1\n    if (L == NULL) {\n        if (tctx.vm_state != NULL) {\n            L = tctx.vm_state->vm;\n        }\n\n        if (L == NULL) {\n            L = lmcf->lua;\n        }\n    }\n#endif\n\n    if (L != NULL) {\n        ngx_http_lua_free_thread(r, L, tctx.co_ref, tctx.co, lmcf);\n    }\n\n    if (tctx.vm_state != NULL) {\n        ngx_http_lua_cleanup_vm(tctx.vm_state);\n    }\n\n    if (c != NULL) {\n        ngx_http_lua_close_fake_connection(c);\n\n    } else if (tctx.pool) {\n        ngx_destroy_pool(tctx.pool);\n    }\n}\n\n\nstatic u_char *\nngx_http_lua_log_timer_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char              *p;\n    ngx_connection_t    *c;\n\n    if (log->action) {\n        p = ngx_snprintf(buf, len, \" while %s\", log->action);\n        len -= p - buf;\n        buf = p;\n    }\n\n    c = log->data;\n\n    dd(\"ctx = %p\", c);\n\n    p = ngx_snprintf(buf, len, \", context: ngx.timer\");\n    len -= p - buf;\n    buf = p;\n\n    if (c != NULL) {\n        if (c->addr_text.len) {\n            p = ngx_snprintf(buf, len, \", client: %V\", &c->addr_text);\n            len -= p - buf;\n            buf = p;\n        }\n\n        if (c->listening && c->listening->addr_text.len) {\n            p = ngx_snprintf(buf, len, \", server: %V\",\n                             &c->listening->addr_text);\n            /* len -= p - buf; */\n            buf = p;\n        }\n    }\n\n    return buf;\n}\n\n\nstatic void\nngx_http_lua_abort_pending_timers(ngx_event_t *ev)\n{\n    ngx_int_t                    i, n;\n    ngx_event_t                **events;\n    ngx_connection_t            *c, *saved_c = NULL;\n    ngx_rbtree_node_t           *cur, *prev, *next, *sentinel, *temp;\n    ngx_http_lua_timer_ctx_t    *tctx;\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua abort pending timers\");\n\n    c = ev->data;\n    lmcf = c->data;\n\n    dd(\"lua connection fd: %d\", (int) c->fd);\n\n    if (!c->close) {\n        return;\n    }\n\n    c->read->closed = 1;\n    c->write->closed = 1;\n\n    /* we temporarily use a valid fd (0) to make ngx_free_connection happy */\n\n    c->fd = 0;\n\n    if (ngx_cycle->files) {\n        saved_c = ngx_cycle->files[0];\n    }\n\n    ngx_free_connection(c);\n\n    c->fd = (ngx_socket_t) -1;\n\n    if (ngx_cycle->files) {\n        ngx_cycle->files[0] = saved_c;\n    }\n\n    if (lmcf->pending_timers == 0) {\n        return;\n    }\n\n    /* expire pending timers immediately */\n\n    sentinel = ngx_event_timer_rbtree.sentinel;\n\n    cur = ngx_event_timer_rbtree.root;\n\n    /* XXX nginx does not guarantee the parent of root is meaningful,\n     * so we temporarily override it to simplify tree traversal. */\n    temp = cur->parent;\n    cur->parent = NULL;\n\n    prev = NULL;\n\n    events = ngx_pcalloc(ngx_cycle->pool,\n                         lmcf->pending_timers * sizeof(ngx_event_t *));\n    if (events == NULL) {\n        return;\n    }\n\n    n = 0;\n\n    dd(\"root: %p, root parent: %p, sentinel: %p\", cur, cur->parent, sentinel);\n\n    while (n < lmcf->pending_timers) {\n        if  (cur == sentinel || cur == NULL) {\n            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                          \"lua pending timer counter got out of sync: %i\",\n                          lmcf->pending_timers);\n            break;\n        }\n\n        dd(\"prev: %p, cur: %p, cur parent: %p, cur left: %p, cur right: %p\",\n           prev, cur, cur->parent, cur->left, cur->right);\n\n        if (prev == cur->parent) {\n            /* neither of the children has been accessed yet */\n\n            next = cur->left;\n            if (next == sentinel) {\n                ev = (ngx_event_t *)\n                    ((char *) cur - offsetof(ngx_event_t, timer));\n\n                if (ev->handler == ngx_http_lua_timer_handler) {\n                    dd(\"found node: %p\", cur);\n                    events[n++] = ev;\n                }\n\n                next = (cur->right != sentinel) ? cur->right : cur->parent;\n            }\n\n        } else if (prev == cur->left) {\n            /* just accessed the left child */\n\n            ev = (ngx_event_t *)\n                ((char *) cur - offsetof(ngx_event_t, timer));\n\n            if (ev->handler == ngx_http_lua_timer_handler) {\n                dd(\"found node 2: %p\", cur);\n                events[n++] = ev;\n            }\n\n            next = (cur->right != sentinel) ? cur->right : cur->parent;\n\n        } else if (prev == cur->right) {\n            /* already accessed both children */\n            next = cur->parent;\n\n        } else {\n            /* not reachable */\n            next = NULL;\n        }\n\n        prev = cur;\n        cur = next;\n    }\n\n    /* restore the old tree root's parent */\n    ngx_event_timer_rbtree.root->parent = temp;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"lua found %i pending timers to be aborted prematurely\",\n                   n);\n\n    for (i = 0; i < n; i++) {\n        ev = events[i];\n\n        ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);\n\n#if (NGX_DEBUG)\n        ev->timer.left = NULL;\n        ev->timer.right = NULL;\n        ev->timer.parent = NULL;\n#endif\n\n        ev->timer_set = 0;\n\n        ev->timedout = 1;\n\n        tctx = ev->data;\n        tctx->premature = 1;\n\n        dd(\"calling timer handler prematurely\");\n        ev->handler(ev);\n    }\n\n#if 0\n    if (pending_timers) {\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                      \"lua pending timer counter got out of sync: %i\",\n                      pending_timers);\n    }\n#endif\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_timer.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_TIMER_H_INCLUDED_\n#define _NGX_HTTP_LUA_TIMER_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_timer_api(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_TIMER_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_uri.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_uri.h\"\n#include \"ngx_http_lua_util.h\"\n\n\nstatic int ngx_http_lua_ngx_req_set_uri(lua_State *L);\n\n\nvoid\nngx_http_lua_inject_req_uri_api(ngx_log_t *log, lua_State *L)\n{\n    lua_pushcfunction(L, ngx_http_lua_ngx_req_set_uri);\n    lua_setfield(L, -2, \"set_uri\");\n}\n\n\nstatic int\nngx_http_lua_ngx_req_set_uri(lua_State *L)\n{\n    ngx_http_request_t          *r;\n    size_t                       len;\n    u_char                      *p;\n    u_char                       byte;\n    int                          n;\n    int                          jump = 0;\n    int                          binary = 0;\n    ngx_http_lua_ctx_t          *ctx;\n    size_t                       buf_len;\n    u_char                      *buf;\n\n    n = lua_gettop(L);\n\n    if (n < 1 || n > 3) {\n        return luaL_error(L, \"expecting 1, 2 or 3 arguments but seen %d\", n);\n    }\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ngx_http_lua_check_fake_request(L, r);\n\n    p = (u_char *) luaL_checklstring(L, 1, &len);\n\n    if (len == 0) {\n        return luaL_error(L, \"attempt to use zero-length uri\");\n    }\n\n    if (n >= 3) {\n        luaL_checktype(L, 3, LUA_TBOOLEAN);\n        binary = lua_toboolean(L, 3);\n    }\n\n    if (!binary\n        && ngx_http_lua_check_unsafe_uri_bytes(r, p, len, &byte) != NGX_OK)\n    {\n        buf_len = ngx_http_lua_escape_log(NULL, p, len) + 1;\n        buf = ngx_palloc(r->pool, buf_len);\n        if (buf == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_lua_escape_log(buf, p, len);\n        buf[buf_len - 1] = '\\0';\n\n        return luaL_error(L, \"unsafe byte \\\"0x%02x\\\" in uri \\\"%s\\\" \"\n                          \"(maybe you want to set the 'binary' argument?)\",\n                          byte, buf);\n    }\n\n    if (n >= 2) {\n        luaL_checktype(L, 2, LUA_TBOOLEAN);\n        jump = lua_toboolean(L, 2);\n\n        if (jump) {\n\n            ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n            if (ctx == NULL) {\n                return luaL_error(L, \"no ctx found\");\n            }\n\n            dd(\"server_rewrite: %d, rewrite: %d, access: %d, \"\n               \"precontent: %d, content: %d\",\n               (int) ctx->entered_server_rewrite_phase,\n               (int) ctx->entered_rewrite_phase,\n               (int) ctx->entered_access_phase,\n               (int) ctx->entered_precontent_phase,\n               (int) ctx->entered_content_phase);\n\n            ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE\n                                       | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE);\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"lua set uri jump to \\\"%*s\\\"\", len, p);\n\n            ngx_http_lua_check_if_abortable(L, ctx);\n        }\n    }\n\n    r->uri.data = ngx_palloc(r->pool, len);\n    if (r->uri.data == NULL) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    ngx_memcpy(r->uri.data, p, len);\n\n    r->uri.len = len;\n\n    r->internal = 1;\n    r->valid_unparsed_uri = 0;\n\n    ngx_http_set_exten(r);\n\n    if (jump) {\n        r->uri_changed = 1;\n\n        return lua_yield(L, 0);\n    }\n\n    r->valid_location = 0;\n    r->uri_changed = 0;\n\n    return 0;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_uri.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_URI_H_INCLUDED_\n#define _NGX_HTTP_LUA_URI_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_req_uri_api(ngx_log_t *log, lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_URI_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_uthread.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_uthread.h\"\n#include \"ngx_http_lua_coroutine.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_probe.h\"\n\n\n#if 1\n#undef ngx_http_lua_probe_info\n#define ngx_http_lua_probe_info(msg)\n#endif\n\n\nstatic int ngx_http_lua_uthread_spawn(lua_State *L);\nstatic int ngx_http_lua_uthread_wait(lua_State *L);\nstatic int ngx_http_lua_uthread_kill(lua_State *L);\n\n\nvoid\nngx_http_lua_inject_uthread_api(ngx_log_t *log, lua_State *L)\n{\n    /* new thread table */\n    lua_createtable(L, 0 /* narr */, 3 /* nrec */);\n\n    lua_pushcfunction(L, ngx_http_lua_uthread_spawn);\n    lua_setfield(L, -2, \"spawn\");\n\n    lua_pushcfunction(L, ngx_http_lua_uthread_wait);\n    lua_setfield(L, -2, \"wait\");\n\n    lua_pushcfunction(L, ngx_http_lua_uthread_kill);\n    lua_setfield(L, -2, \"kill\");\n\n    lua_setfield(L, -2, \"thread\");\n}\n\n\nstatic int\nngx_http_lua_uthread_spawn(lua_State *L)\n{\n    int                           n, co_ref;\n    ngx_http_request_t           *r;\n    ngx_http_lua_ctx_t           *ctx;\n    ngx_http_lua_co_ctx_t        *coctx = NULL;\n\n    n = lua_gettop(L);\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    ngx_http_lua_coroutine_create_helper(L, r, ctx, &coctx, &co_ref);\n\n    /* anchor the newly created coroutine into the Lua registry */\n\n    if (n > 1) {\n        lua_replace(L, 1);\n        lua_xmove(L, coctx->co, n - 1);\n    }\n\n    coctx->co_ref = co_ref;\n    coctx->is_uthread = 1;\n    ctx->uthreads++;\n\n    coctx->co_status = NGX_HTTP_LUA_CO_RUNNING;\n    ctx->co_op = NGX_HTTP_LUA_USER_THREAD_RESUME;\n\n    ctx->cur_co_ctx->thread_spawn_yielded = 1;\n\n    if (ngx_http_lua_post_thread(r, ctx, ctx->cur_co_ctx) != NGX_OK) {\n        return luaL_error(L, \"no memory\");\n    }\n\n    coctx->parent_co_ctx = ctx->cur_co_ctx;\n    ctx->cur_co_ctx = coctx;\n\n    ngx_http_lua_attach_co_ctx_to_L(coctx->co, coctx);\n\n    ngx_http_lua_probe_user_thread_spawn(r, L, coctx->co);\n\n    dd(\"yielding with arg %s, top=%d, index-1:%s\", luaL_typename(L, -1),\n       (int) lua_gettop(L), luaL_typename(L, 1));\n    return lua_yield(L, 1);\n}\n\n\nstatic int\nngx_http_lua_uthread_wait(lua_State *L)\n{\n    int                          i, nargs, nrets;\n    lua_State                   *sub_co;\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx, *sub_coctx;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    coctx = ctx->cur_co_ctx;\n\n    nargs = lua_gettop(L);\n    if (nargs == 0) {\n        return luaL_error(L, \"at least one coroutine should be specified\");\n    }\n\n    for (i = 1; i <= nargs; i++) {\n        sub_co = lua_tothread(L, i);\n\n        luaL_argcheck(L, sub_co, i, \"lua thread expected\");\n\n        sub_coctx = ngx_http_lua_get_co_ctx(sub_co, ctx);\n        if (sub_coctx == NULL) {\n            return luaL_error(L, \"no co ctx found\");\n        }\n\n        if (!sub_coctx->is_uthread) {\n            return luaL_error(L, \"attempt to wait on a coroutine that is \"\n                              \"not a user thread\");\n        }\n\n        if (sub_coctx->parent_co_ctx != coctx) {\n            return luaL_error(L, \"only the parent coroutine can wait on the \"\n                              \"thread\");\n        }\n\n        switch (sub_coctx->co_status) {\n        case NGX_HTTP_LUA_CO_ZOMBIE:\n\n            ngx_http_lua_probe_info(\"found zombie child\");\n\n            nrets = lua_gettop(sub_coctx->co);\n\n            dd(\"child retval count: %d, %s: %s\", (int) nrets,\n               luaL_typename(sub_coctx->co, -1),\n               lua_tostring(sub_coctx->co, -1));\n\n            if (nrets) {\n                lua_xmove(sub_coctx->co, L, nrets);\n            }\n\n#if 1\n            ngx_http_lua_del_thread(r, L, ctx, sub_coctx);\n            ctx->uthreads--;\n#endif\n\n            return nrets;\n\n        case NGX_HTTP_LUA_CO_DEAD:\n            dd(\"uthread already waited: %p (parent %p)\", sub_coctx,\n               coctx);\n\n            if (i < nargs) {\n                /* just ignore it if it is not the last one */\n                continue;\n            }\n\n            /* being the last one */\n            lua_pushnil(L);\n            lua_pushliteral(L, \"already waited or killed\");\n            return 2;\n\n        default:\n            dd(\"uthread %p still alive, status: %d, parent %p\", sub_coctx,\n               sub_coctx->co_status, coctx);\n            break;\n        }\n\n        ngx_http_lua_probe_user_thread_wait(L, sub_coctx->co);\n        sub_coctx->waited_by_parent = 1;\n    }\n\n    return lua_yield(L, 0);\n}\n\n\nstatic int\nngx_http_lua_uthread_kill(lua_State *L)\n{\n    lua_State                   *sub_co;\n    ngx_http_request_t          *r;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_lua_co_ctx_t       *coctx, *sub_coctx;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no request ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    coctx = ctx->cur_co_ctx;\n\n    sub_co = lua_tothread(L, 1);\n    luaL_argcheck(L, sub_co, 1, \"lua thread expected\");\n\n    sub_coctx = ngx_http_lua_get_co_ctx(sub_co, ctx);\n\n    if (sub_coctx == NULL) {\n        return luaL_error(L, \"no co ctx found\");\n    }\n\n    if (!sub_coctx->is_uthread) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"not user thread\");\n        return 2;\n    }\n\n    if (sub_coctx->parent_co_ctx != coctx) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"killer not parent\");\n        return 2;\n    }\n\n    if (sub_coctx->pending_subreqs > 0) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"pending subrequests\");\n        return 2;\n    }\n\n    switch (sub_coctx->co_status) {\n    case NGX_HTTP_LUA_CO_ZOMBIE:\n        ngx_http_lua_del_thread(r, L, ctx, sub_coctx);\n        ctx->uthreads--;\n\n        lua_pushnil(L);\n        lua_pushliteral(L, \"already terminated\");\n        return 2;\n\n    case NGX_HTTP_LUA_CO_DEAD:\n        lua_pushnil(L);\n        lua_pushliteral(L, \"already waited or killed\");\n        return 2;\n\n    default:\n        ngx_http_lua_cleanup_pending_operation(sub_coctx);\n        ngx_http_lua_del_thread(r, L, ctx, sub_coctx);\n        ctx->uthreads--;\n\n        lua_pushinteger(L, 1);\n        return 1;\n    }\n\n    /* not reachable */\n}\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_uthread.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_UTHREAD_H_INCLUDED_\n#define _NGX_HTTP_LUA_UTHREAD_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\n#define ngx_http_lua_is_thread(ctx)                                          \\\n    ((ctx)->cur_co_ctx->is_uthread || (ctx)->cur_co_ctx == &(ctx)->entry_co_ctx)\n\n\n#define ngx_http_lua_is_entry_thread(ctx)                                    \\\n    ((ctx)->cur_co_ctx == &(ctx)->entry_co_ctx)\n\n\n#define ngx_http_lua_entry_thread_alive(ctx)                                 \\\n    ((ctx)->entry_co_ctx.co_ref != LUA_NOREF)\n\n\n#define ngx_http_lua_coroutine_alive(coctx)                                  \\\n    ((coctx)->co_status != NGX_HTTP_LUA_CO_DEAD                              \\\n     && (coctx)->co_status != NGX_HTTP_LUA_CO_ZOMBIE)\n\n\nvoid ngx_http_lua_inject_uthread_api(ngx_log_t *log, lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_UTHREAD_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_util.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"nginx.h\"\n#include \"ngx_http_lua_directive.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_exception.h\"\n#include \"ngx_http_lua_pcrefix.h\"\n#include \"ngx_http_lua_args.h\"\n#include \"ngx_http_lua_uri.h\"\n#include \"ngx_http_lua_req_body.h\"\n#include \"ngx_http_lua_headers.h\"\n#include \"ngx_http_lua_output.h\"\n#include \"ngx_http_lua_control.h\"\n#include \"ngx_http_lua_ndk.h\"\n#include \"ngx_http_lua_subrequest.h\"\n#include \"ngx_http_lua_log.h\"\n#include \"ngx_http_lua_string.h\"\n#include \"ngx_http_lua_misc.h\"\n#include \"ngx_http_lua_consts.h\"\n#include \"ngx_http_lua_shdict.h\"\n#include \"ngx_http_lua_coroutine.h\"\n#include \"ngx_http_lua_socket_tcp.h\"\n#include \"ngx_http_lua_socket_udp.h\"\n#include \"ngx_http_lua_sleep.h\"\n#include \"ngx_http_lua_setby.h\"\n#include \"ngx_http_lua_headerfilterby.h\"\n#include \"ngx_http_lua_bodyfilterby.h\"\n#include \"ngx_http_lua_logby.h\"\n#include \"ngx_http_lua_probe.h\"\n#include \"ngx_http_lua_uthread.h\"\n#include \"ngx_http_lua_contentby.h\"\n#include \"ngx_http_lua_timer.h\"\n#include \"ngx_http_lua_config.h\"\n#include \"ngx_http_lua_socket_tcp.h\"\n#include \"ngx_http_lua_ssl_certby.h\"\n#include \"ngx_http_lua_ssl.h\"\n#include \"ngx_http_lua_log_ringbuf.h\"\n#if (NGX_THREADS)\n#include \"ngx_http_lua_worker_thread.h\"\n#endif\n#if (NGX_HTTP_V3)\n#include <ngx_event_quic.h>\n#include <ngx_event_quic_connection.h>\n#endif\n\n\n#if 1\n#undef ngx_http_lua_probe_info\n#define ngx_http_lua_probe_info(msg)\n#endif\n\n\n#ifndef NGX_HTTP_LUA_BT_DEPTH\n#define NGX_HTTP_LUA_BT_DEPTH  22\n#endif\n\n\n#ifndef NGX_HTTP_LUA_BT_MAX_COROS\n#define NGX_HTTP_LUA_BT_MAX_COROS  5\n#endif\n\n\n#if (NGX_HTTP_LUA_HAVE_SA_RESTART)\n#define NGX_HTTP_LUA_SA_RESTART_SIGS {                                       \\\n    ngx_signal_value(NGX_RECONFIGURE_SIGNAL),                                \\\n    ngx_signal_value(NGX_REOPEN_SIGNAL),                                     \\\n    ngx_signal_value(NGX_NOACCEPT_SIGNAL),                                   \\\n    ngx_signal_value(NGX_TERMINATE_SIGNAL),                                  \\\n    ngx_signal_value(NGX_SHUTDOWN_SIGNAL),                                   \\\n    ngx_signal_value(NGX_CHANGEBIN_SIGNAL),                                  \\\n    SIGALRM,                                                                 \\\n    SIGINT,                                                                  \\\n    SIGIO,                                                                   \\\n    SIGCHLD,                                                                 \\\n    SIGSYS,                                                                  \\\n    SIGPIPE,                                                                 \\\n    0                                                                        \\\n};\n#endif\n\n\nchar ngx_http_lua_code_cache_key;\nchar ngx_http_lua_socket_pool_key;\nchar ngx_http_lua_coroutines_key;\nchar ngx_http_lua_headers_metatable_key;\n\n\nngx_uint_t  ngx_http_lua_location_hash = 0;\nngx_uint_t  ngx_http_lua_content_length_hash = 0;\n\n\nstatic ngx_int_t ngx_http_lua_send_http10_headers(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx);\nstatic void ngx_http_lua_init_registry(lua_State *L, ngx_log_t *log);\nstatic void ngx_http_lua_init_globals(lua_State *L, ngx_cycle_t *cycle,\n    ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log);\n#ifdef OPENRESTY_LUAJIT\nstatic void ngx_http_lua_inject_global_write_guard(lua_State *L,\n    ngx_log_t *log);\n#endif\nstatic void ngx_http_lua_set_path(ngx_cycle_t *cycle, lua_State *L, int tab_idx,\n    const char *fieldname, const char *path, const char *default_path,\n    ngx_log_t *log);\nstatic ngx_int_t ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx);\nstatic ngx_int_t ngx_http_lua_handle_exit(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx);\nstatic ngx_int_t ngx_http_lua_handle_rewrite_jump(lua_State *L,\n    ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx);\nstatic int ngx_http_lua_thread_traceback(lua_State *L, lua_State *co,\n    ngx_http_lua_co_ctx_t *coctx);\nstatic void ngx_http_lua_inject_ngx_api(lua_State *L,\n    ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log);\nstatic void ngx_http_lua_inject_arg_api(lua_State *L);\nstatic int ngx_http_lua_param_set(lua_State *L);\nstatic ngx_int_t ngx_http_lua_output_filter(ngx_http_request_t *r,\n    ngx_chain_t *in);\nstatic ngx_int_t ngx_http_lua_send_special(ngx_http_request_t *r,\n    ngx_uint_t flags);\nstatic void ngx_http_lua_finalize_threads(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, lua_State *L);\nstatic ngx_int_t ngx_http_lua_post_zombie_thread(ngx_http_request_t *r,\n    ngx_http_lua_co_ctx_t *parent, ngx_http_lua_co_ctx_t *thread);\nstatic void ngx_http_lua_cleanup_zombie_child_uthreads(ngx_http_request_t *r,\n    lua_State *L, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx);\nstatic ngx_int_t ngx_http_lua_on_abort_resume(ngx_http_request_t *r);\nstatic void ngx_http_lua_close_fake_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_lua_flush_pending_output(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx);\nstatic ngx_int_t\n    ngx_http_lua_process_flushing_coroutines(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx);\nstatic lua_State *ngx_http_lua_new_state(lua_State *parent_vm,\n    ngx_cycle_t *cycle, ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log);\nstatic int ngx_http_lua_get_raw_phase_context(lua_State *L);\n\n\n#ifndef LUA_PATH_SEP\n#define LUA_PATH_SEP \";\"\n#endif\n\n\n#if !defined(LUA_DEFAULT_PATH) && (NGX_DEBUG)\n#define LUA_DEFAULT_PATH \"../lua-resty-core/lib/?.lua;\"                      \\\n                         \"../lua-resty-lrucache/lib/?.lua\"\n#endif\n\n\n#define AUX_MARK \"\\1\"\n\n\nstatic void\nngx_http_lua_set_path(ngx_cycle_t *cycle, lua_State *L, int tab_idx,\n    const char *fieldname, const char *path, const char *default_path,\n    ngx_log_t *log)\n{\n    const char          *tmp_path;\n    const char          *prefix;\n\n    /* XXX here we use some hack to simplify string manipulation */\n    tmp_path = luaL_gsub(L, path, LUA_PATH_SEP LUA_PATH_SEP,\n                         LUA_PATH_SEP AUX_MARK LUA_PATH_SEP);\n\n    lua_pushlstring(L, (char *) cycle->prefix.data, cycle->prefix.len);\n    prefix = lua_tostring(L, -1);\n    tmp_path = luaL_gsub(L, tmp_path, \"$prefix\", prefix);\n    tmp_path = luaL_gsub(L, tmp_path, \"${prefix}\", prefix);\n    lua_pop(L, 3);\n\n    dd(\"tmp_path path: %s\", tmp_path);\n\n#if (NGX_DEBUG)\n    tmp_path =\n#else\n    (void)\n#endif\n        luaL_gsub(L, tmp_path, AUX_MARK, default_path);\n\n#if (NGX_DEBUG)\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"lua setting lua package.%s to \\\"%s\\\"\", fieldname, tmp_path);\n#endif\n\n    lua_remove(L, -2);\n\n    /* fix negative index as there's new data on stack */\n    tab_idx = (tab_idx < 0) ? (tab_idx - 1) : tab_idx;\n    lua_setfield(L, tab_idx, fieldname);\n}\n\n\n#ifndef OPENRESTY_LUAJIT\n/**\n * Create new table and set _G field to itself.\n *\n * After:\n *         | new table | <- top\n *         |    ...    |\n * */\nvoid\nngx_http_lua_create_new_globals_table(lua_State *L, int narr, int nrec)\n{\n    lua_createtable(L, narr, nrec + 1);\n    lua_pushvalue(L, -1);\n    lua_setfield(L, -2, \"_G\");\n}\n#endif /* OPENRESTY_LUAJIT */\n\n\nstatic lua_State *\nngx_http_lua_new_state(lua_State *parent_vm, ngx_cycle_t *cycle,\n    ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log)\n{\n    lua_State       *L;\n    const char      *old_path;\n    const char      *new_path;\n    size_t           old_path_len;\n    const char      *old_cpath;\n    const char      *new_cpath;\n    size_t           old_cpath_len;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, \"lua creating new vm state\");\n\n    L = luaL_newstate();\n    if (L == NULL) {\n        return NULL;\n    }\n\n    luaL_openlibs(L);\n\n    lua_getglobal(L, \"package\");\n\n    if (!lua_istable(L, -1)) {\n        ngx_log_error(NGX_LOG_EMERG, log, 0,\n                      \"the \\\"package\\\" table does not exist\");\n        return NULL;\n    }\n\n    if (parent_vm) {\n        lua_getglobal(parent_vm, \"package\");\n        lua_getfield(parent_vm, -1, \"path\");\n        old_path = lua_tolstring(parent_vm, -1, &old_path_len);\n        lua_pop(parent_vm, 1);\n\n        lua_pushlstring(L, old_path, old_path_len);\n        lua_setfield(L, -2, \"path\");\n\n        lua_getfield(parent_vm, -1, \"cpath\");\n        old_path = lua_tolstring(parent_vm, -1, &old_path_len);\n        lua_pop(parent_vm, 2);\n\n        lua_pushlstring(L, old_path, old_path_len);\n        lua_setfield(L, -2, \"cpath\");\n\n    } else {\n#ifdef LUA_DEFAULT_PATH\n#   define LUA_DEFAULT_PATH_LEN (sizeof(LUA_DEFAULT_PATH) - 1)\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,\n                       \"lua prepending default package.path with %s\",\n                       LUA_DEFAULT_PATH);\n\n        lua_pushliteral(L, LUA_DEFAULT_PATH \";\"); /* package default */\n        lua_getfield(L, -2, \"path\"); /* package default old */\n        lua_concat(L, 2); /* package new */\n        lua_setfield(L, -2, \"path\"); /* package */\n#endif\n\n#ifdef LUA_DEFAULT_CPATH\n#   define LUA_DEFAULT_CPATH_LEN (sizeof(LUA_DEFAULT_CPATH) - 1)\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,\n                       \"lua prepending default package.cpath with %s\",\n                       LUA_DEFAULT_CPATH);\n\n        lua_pushliteral(L, LUA_DEFAULT_CPATH \";\"); /* package default */\n        lua_getfield(L, -2, \"cpath\"); /* package default old */\n        lua_concat(L, 2); /* package new */\n        lua_setfield(L, -2, \"cpath\"); /* package */\n#endif\n\n        if (lmcf->lua_path.len != 0) {\n            lua_getfield(L, -1, \"path\"); /* get original package.path */\n            old_path = lua_tolstring(L, -1, &old_path_len);\n\n            dd(\"old path: %s\", old_path);\n\n            lua_pushlstring(L, (char *) lmcf->lua_path.data,\n                            lmcf->lua_path.len);\n            new_path = lua_tostring(L, -1);\n\n            ngx_http_lua_set_path(cycle, L, -3, \"path\", new_path, old_path,\n                                  log);\n\n            lua_pop(L, 2);\n        }\n\n        if (lmcf->lua_cpath.len != 0) {\n            lua_getfield(L, -1, \"cpath\"); /* get original package.cpath */\n            old_cpath = lua_tolstring(L, -1, &old_cpath_len);\n\n            dd(\"old cpath: %s\", old_cpath);\n\n            lua_pushlstring(L, (char *) lmcf->lua_cpath.data,\n                            lmcf->lua_cpath.len);\n            new_cpath = lua_tostring(L, -1);\n\n            ngx_http_lua_set_path(cycle, L, -3, \"cpath\", new_cpath, old_cpath,\n                                  log);\n\n\n            lua_pop(L, 2);\n        }\n    }\n\n    lua_pop(L, 1); /* remove the \"package\" table */\n\n    ngx_http_lua_init_registry(L, log);\n    ngx_http_lua_init_globals(L, cycle, lmcf, log);\n\n    return L;\n}\n\n\nlua_State *\nngx_http_lua_new_thread(ngx_http_request_t *r, lua_State *L, int *ref)\n{\n    int              base;\n    lua_State       *co;\n\n#ifdef HAVE_LUA_RESETTHREAD\n    ngx_queue_t     *q;\n\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_http_lua_thread_ref_t   *tref;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua creating new thread\");\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    if (L == lmcf->lua && !ngx_queue_empty(&lmcf->cached_lua_threads)) {\n        q = ngx_queue_head(&lmcf->cached_lua_threads);\n        tref = ngx_queue_data(q, ngx_http_lua_thread_ref_t, queue);\n\n        ngx_http_lua_assert(tref->ref != LUA_NOREF);\n        ngx_http_lua_assert(tref->co != NULL);\n\n        co = tref->co;\n        *ref = tref->ref;\n\n        tref->co = NULL;\n        tref->ref = LUA_NOREF;\n\n        ngx_queue_remove(q);\n        ngx_queue_insert_head(&lmcf->free_lua_threads, q);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua reusing cached lua thread %p (ref %d)\", co, *ref);\n\n#if 0\n        {\n            int n = 0;\n            lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                                  coroutines_key));\n            lua_rawget(L, LUA_REGISTRYINDEX);\n            lua_pushnil(L);  /* first key */\n            while (lua_next(L, -2) != 0) {\n                if (!lua_isnil(L, -1) && !lua_isnil(L, -2)) {\n                    n++;\n                }\n\n                lua_pop(L, 1);\n            }\n\n            lua_pop(L, 1);\n\n            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"! lua reusing cached lua thread %p (ref %d, n %d)\",\n                           co, *ref, n);\n        }\n#endif\n\n    } else\n#endif\n    {\n        base = lua_gettop(L);\n\n        lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                              coroutines_key));\n        lua_rawget(L, LUA_REGISTRYINDEX);\n\n        co = lua_newthread(L);\n\n#ifndef OPENRESTY_LUAJIT\n        /*  {{{ inherit coroutine's globals to main thread's globals table\n         *  for print() function will try to find tostring() in current\n         *  globals table.\n         */\n        /*  new globals table for coroutine */\n        ngx_http_lua_create_new_globals_table(co, 0, 0);\n\n        lua_createtable(co, 0, 1);\n        ngx_http_lua_get_globals_table(co);\n        lua_setfield(co, -2, \"__index\");\n        lua_setmetatable(co, -2);\n\n        ngx_http_lua_set_globals_table(co);\n        /*  }}} */\n#endif /* OPENRESTY_LUAJIT */\n\n        *ref = luaL_ref(L, -2);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP,\n                       ngx_cycle->log, 0, \"lua ref lua thread %p (ref %d)\", co,\n                       *ref);\n\n        if (*ref == LUA_NOREF) {\n            lua_settop(L, base);  /* restore main thread stack */\n            return NULL;\n        }\n\n        lua_settop(L, base);\n    }\n\n    return co;\n}\n\n\nvoid\nngx_http_lua_del_thread(ngx_http_request_t *r, lua_State *L,\n    ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx)\n{\n#ifdef HAVE_LUA_RESETTHREAD\n    ngx_queue_t                 *q;\n    ngx_http_lua_main_conf_t    *lmcf;\n    ngx_http_lua_thread_ref_t   *tref;\n#endif\n\n    if (coctx->co_ref == LUA_NOREF) {\n        return;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua deleting light thread %p (ref %d)\", coctx->co,\n                   coctx->co_ref);\n\n    ngx_http_lua_probe_thread_delete(r, coctx->co, ctx);\n\n#ifdef HAVE_LUA_RESETTHREAD\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    if (ctx != NULL\n        && coctx->co == ctx->entry_co_ctx.co\n        && L == lmcf->lua && !ngx_queue_empty(&lmcf->free_lua_threads))\n    {\n        lua_resetthread(L, coctx->co);\n        q = ngx_queue_head(&lmcf->free_lua_threads);\n        tref = ngx_queue_data(q, ngx_http_lua_thread_ref_t, queue);\n\n        ngx_http_lua_assert(tref->ref == LUA_NOREF);\n        ngx_http_lua_assert(tref->co == NULL);\n\n        tref->ref = coctx->co_ref;\n        tref->co = coctx->co;\n\n        ngx_queue_remove(q);\n        ngx_queue_insert_head(&lmcf->cached_lua_threads, q);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua caching unused lua thread %p (ref %d)\", coctx->co,\n                       coctx->co_ref);\n\n    } else {\n#endif\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http lua unref thread %p: %d\", coctx->co,\n                       coctx->co_ref);\n\n        lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                              coroutines_key));\n        lua_rawget(L, LUA_REGISTRYINDEX);\n        luaL_unref(L, -1, coctx->co_ref);\n        lua_pop(L, 1);\n#ifdef HAVE_LUA_RESETTHREAD\n    }\n#endif\n\n    coctx->co_ref = LUA_NOREF;\n    coctx->co_status = NGX_HTTP_LUA_CO_DEAD;\n}\n\n\nu_char *\nngx_http_lua_rebase_path(ngx_pool_t *pool, u_char *src, size_t len)\n{\n    u_char     *p;\n    ngx_str_t   dst;\n\n    dst.data = ngx_palloc(pool, len + 1);\n    if (dst.data == NULL) {\n        return NULL;\n    }\n\n    dst.len = len;\n\n    p = ngx_copy(dst.data, src, len);\n    *p = '\\0';\n\n    if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->prefix, &dst)\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    return dst.data;\n}\n\n\nngx_int_t\nngx_http_lua_send_header_if_needed(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx)\n{\n    ngx_int_t            rc;\n\n    dd(\"send header if needed: %d\", r->header_sent || ctx->header_sent);\n\n    if (!r->header_sent && !ctx->header_sent) {\n        if (r->headers_out.status == 0) {\n            r->headers_out.status = NGX_HTTP_OK;\n        }\n\n        if (!ctx->mime_set\n            && ngx_http_lua_set_content_type(r, ctx) != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        if (!ctx->headers_set) {\n            ngx_http_clear_content_length(r);\n            ngx_http_clear_accept_ranges(r);\n        }\n\n        if (!ctx->buffering) {\n            dd(\"sending headers\");\n            rc = ngx_http_send_header(r);\n            if (r->filter_finalize) {\n                ngx_http_set_ctx(r, ctx, ngx_http_lua_module);\n            }\n\n            ctx->header_sent = 1;\n            return rc;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_lua_send_chain_link(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx,\n    ngx_chain_t *in)\n{\n    ngx_int_t                     rc;\n    ngx_chain_t                  *cl;\n    ngx_chain_t                 **ll;\n    ngx_http_lua_loc_conf_t      *llcf;\n\n#if 1\n    if (ctx->acquired_raw_req_socket || ctx->eof) {\n        dd(\"ctx->eof already set or raw req socket already acquired\");\n        return NGX_OK;\n    }\n#endif\n\n    if ((r->method & NGX_HTTP_HEAD) && !r->header_only) {\n        r->header_only = 1;\n    }\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (llcf->http10_buffering\n        && !ctx->buffering\n        && !r->header_sent\n        && !ctx->header_sent\n        && r->http_version < NGX_HTTP_VERSION_11\n        && r->headers_out.content_length_n < 0)\n    {\n        ctx->buffering = 1;\n    }\n\n    rc = ngx_http_lua_send_header_if_needed(r, ctx);\n\n    if (rc == NGX_ERROR || rc > NGX_OK) {\n        return rc;\n    }\n\n    if (r->header_only) {\n        ctx->eof = 1;\n\n        if (!r->request_body && r == r->main) {\n            if (ngx_http_discard_request_body(r) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n\n        if (ctx->buffering) {\n            return ngx_http_lua_send_http10_headers(r, ctx);\n        }\n\n        return rc;\n    }\n\n    if (in == NULL) {\n        dd(\"last buf to be sent\");\n\n#if 1\n        if (!r->request_body && r == r->main) {\n            if (ngx_http_discard_request_body(r) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n#endif\n\n        if (ctx->buffering) {\n            rc = ngx_http_lua_send_http10_headers(r, ctx);\n            if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n                return rc;\n            }\n\n            if (ctx->out) {\n\n                rc = ngx_http_lua_output_filter(r, ctx->out);\n\n                if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n                    return rc;\n                }\n\n                ctx->out = NULL;\n            }\n        }\n\n        ctx->eof = 1;\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua sending last buf of the response body\");\n\n        rc = ngx_http_lua_send_special(r, NGX_HTTP_LAST);\n\n        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n\n        return NGX_OK;\n    }\n\n    /* in != NULL */\n\n    if (ctx->buffering) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"lua buffering output bufs for the HTTP 1.0 request\");\n\n        for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {\n            ll = &cl->next;\n        }\n\n        *ll = in;\n\n        return NGX_OK;\n    }\n\n    return ngx_http_lua_output_filter(r, in);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_send_special(ngx_http_request_t *r, ngx_uint_t flags)\n{\n    ngx_int_t            rc;\n    ngx_http_request_t  *ar; /* active request */\n\n    ar = r->connection->data;\n\n    if (ar != r) {\n\n        /* bypass ngx_http_postpone_filter_module */\n\n        r->connection->data = r;\n        rc = ngx_http_send_special(r, flags);\n        r->connection->data = ar;\n        return rc;\n    }\n\n    return ngx_http_send_special(r, flags);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_output_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_int_t            rc;\n    ngx_http_lua_ctx_t  *ctx;\n    ngx_http_request_t  *ar; /* active request */\n\n    ar = r->connection->data;\n\n    if (ar != r) {\n\n        /* bypass ngx_http_postpone_filter_module */\n\n        r->connection->data = r;\n        rc = ngx_http_output_filter(r, in);\n        r->connection->data = ar;\n        return rc;\n    }\n\n    rc = ngx_http_output_filter(r, in);\n\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    if (ctx == NULL) {\n        return rc;\n    }\n\n    ngx_chain_update_chains(r->pool,\n                            &ctx->free_bufs, &ctx->busy_bufs, &in,\n                            (ngx_buf_tag_t) &ngx_http_lua_module);\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_send_http10_headers(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx)\n{\n    off_t                size;\n    ngx_chain_t         *cl;\n    ngx_int_t            rc;\n\n    if (r->header_sent || ctx->header_sent) {\n        return NGX_OK;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua sending HTTP 1.0 response headers\");\n\n    if (r->header_only) {\n        goto send;\n    }\n\n    if (r->headers_out.content_length == NULL) {\n        for (size = 0, cl = ctx->out; cl; cl = cl->next) {\n            size += ngx_buf_size(cl->buf);\n        }\n\n        r->headers_out.content_length_n = size;\n    }\n\nsend:\n\n    rc = ngx_http_send_header(r);\n    ctx->header_sent = 1;\n    return rc;\n}\n\n\nstatic void\nngx_http_lua_init_registry(lua_State *L, ngx_log_t *log)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"lua initializing lua registry\");\n\n    /* {{{ register a table to anchor lua coroutines reliably:\n     * {([int]ref) = [cort]} */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          coroutines_key));\n    lua_createtable(L, 0, 32 /* nrec */);\n    lua_rawset(L, LUA_REGISTRYINDEX);\n    /* }}} */\n\n    /*\n     * the the Lua request ctx data table will create in resty.core.ctx,\n     * just equivalent to the following code:\n     *    lua_pushliteral(L, ngx_http_lua_ctx_tables_key);\n     *    lua_createtable(L, 0, 0);\n     *    lua_rawset(L, LUA_REGISTRYINDEX);\n     */\n\n    /* create the registry entry for the Lua socket connection pool table */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          socket_pool_key));\n    lua_createtable(L, 0, 8 /* nrec */);\n    lua_rawset(L, LUA_REGISTRYINDEX);\n\n    /* {{{ register table to cache user code:\n     * { [(string)cache_key] = <code closure> } */\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          code_cache_key));\n    lua_createtable(L, 0, 8 /* nrec */);\n    lua_rawset(L, LUA_REGISTRYINDEX);\n    /* }}} */\n}\n\n\nstatic void\nngx_http_lua_init_globals(lua_State *L, ngx_cycle_t *cycle,\n    ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"lua initializing lua globals\");\n\n#if defined(NDK) && NDK\n    ngx_http_lua_inject_ndk_api(L);\n#endif /* defined(NDK) && NDK */\n\n    ngx_http_lua_inject_ngx_api(L, lmcf, log);\n}\n\n\nstatic void\nngx_http_lua_inject_ngx_api(lua_State *L, ngx_http_lua_main_conf_t *lmcf,\n    ngx_log_t *log)\n{\n    lua_createtable(L, 0 /* narr */, 115 /* nrec */);    /* ngx.* */\n\n    lua_pushcfunction(L, ngx_http_lua_get_raw_phase_context);\n    lua_setfield(L, -2, \"_phase_ctx\");\n\n    ngx_http_lua_inject_arg_api(L);\n\n    ngx_http_lua_inject_http_consts(L);\n    ngx_http_lua_inject_core_consts(L);\n\n    ngx_http_lua_inject_log_api(L);\n    ngx_http_lua_inject_output_api(L);\n    ngx_http_lua_inject_string_api(L);\n    ngx_http_lua_inject_control_api(log, L);\n    ngx_http_lua_inject_subrequest_api(L);\n    ngx_http_lua_inject_sleep_api(L);\n\n    ngx_http_lua_inject_req_api(log, L);\n    ngx_http_lua_inject_resp_header_api(L);\n    ngx_http_lua_create_headers_metatable(log, L);\n    ngx_http_lua_inject_shdict_api(lmcf, L);\n    ngx_http_lua_inject_socket_tcp_api(log, L);\n    ngx_http_lua_inject_socket_udp_api(log, L);\n    ngx_http_lua_inject_uthread_api(log, L);\n    ngx_http_lua_inject_timer_api(L);\n    ngx_http_lua_inject_config_api(L);\n#if (NGX_THREADS)\n    ngx_http_lua_inject_worker_thread_api(log, L);\n#endif\n\n    lua_getglobal(L, \"package\"); /* ngx package */\n    lua_getfield(L, -1, \"loaded\"); /* ngx package loaded */\n    lua_pushvalue(L, -3); /* ngx package loaded ngx */\n    lua_setfield(L, -2, \"ngx\"); /* ngx package loaded */\n    lua_pop(L, 2);\n\n    lua_setglobal(L, \"ngx\");\n\n    ngx_http_lua_inject_coroutine_api(log, L);\n}\n\n\n#ifdef OPENRESTY_LUAJIT\nstatic void\nngx_http_lua_inject_global_write_guard(lua_State *L, ngx_log_t *log)\n{\n    int         rc;\n\n    const char buf[] =\n        \"local ngx_log = ngx.log\\n\"\n        \"local ngx_WARN = ngx.WARN\\n\"\n        \"local tostring = tostring\\n\"\n        \"local ngx_get_phase = ngx.get_phase\\n\"\n        \"local traceback = require 'debug'.traceback\\n\"\n        \"local function newindex(table, key, value)\\n\"\n            \"rawset(table, key, value)\\n\"\n            \"local phase = ngx_get_phase()\\n\"\n            \"if phase == 'init_worker' or phase == 'init' then\\n\"\n                \"return\\n\"\n            \"end\\n\"\n            \"ngx_log(ngx_WARN, 'writing a global Lua variable \"\n                     \"(\\\\'', tostring(key), '\\\\') which may lead to \"\n                     \"race conditions between concurrent requests, so \"\n                     \"prefer the use of \\\\'local\\\\' variables', \"\n                     \"traceback('', 2))\\n\"\n        \"end\\n\"\n        \"setmetatable(_G, { __newindex = newindex })\\n\"\n        ;\n\n    rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, \"=_G write guard\");\n\n    if (rc != 0) {\n        ngx_log_error(NGX_LOG_ERR, log, 0,\n                      \"failed to load Lua code (%i): %s\",\n                      rc, lua_tostring(L, -1));\n\n        lua_pop(L, 1);\n        return;\n    }\n\n    rc = lua_pcall(L, 0, 0, 0);\n    if (rc != 0) {\n        ngx_log_error(NGX_LOG_ERR, log, 0,\n                      \"failed to run Lua code (%i): %s\",\n                      rc, lua_tostring(L, -1));\n        lua_pop(L, 1);\n    }\n}\n#endif\n\n\nvoid\nngx_http_lua_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in)\n{\n    ngx_chain_t         *cl;\n\n    for (cl = in; cl; cl = cl->next) {\n        cl->buf->pos = cl->buf->last;\n        cl->buf->file_pos = cl->buf->file_last;\n    }\n}\n\n\nngx_int_t\nngx_http_lua_add_copy_chain(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx,\n    ngx_chain_t ***plast, ngx_chain_t *in, ngx_int_t *eof)\n{\n    ngx_chain_t     *cl;\n    size_t           len;\n    ngx_buf_t       *b;\n\n    len = 0;\n    *eof = 0;\n\n    for (cl = in; cl; cl = cl->next) {\n        if (ngx_buf_in_memory(cl->buf)) {\n            len += cl->buf->last - cl->buf->pos;\n        }\n\n        if (cl->buf->last_in_chain || cl->buf->last_buf) {\n            *eof = 1;\n        }\n    }\n\n    if (len == 0) {\n        return NGX_OK;\n    }\n\n    cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,\n                                         &ctx->free_bufs, len);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    dd(\"chains get free buf: %d == %d\", (int) (cl->buf->end - cl->buf->start),\n       (int) len);\n\n    b = cl->buf;\n\n    while (in) {\n        if (ngx_buf_in_memory(in->buf)) {\n            b->last = ngx_copy(b->last, in->buf->pos,\n                               in->buf->last - in->buf->pos);\n        }\n\n        in = in->next;\n    }\n\n    **plast = cl;\n    *plast = &cl->next;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_lua_reset_ctx(ngx_http_request_t *r, lua_State *L,\n    ngx_http_lua_ctx_t *ctx)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua reset ctx\");\n\n    ngx_http_lua_finalize_threads(r, ctx, L);\n\n#if 0\n    if (ctx->user_co_ctx) {\n        /* no way to destroy a list but clean up the whole pool */\n        ctx->user_co_ctx = NULL;\n    }\n#endif\n\n    ngx_memzero(&ctx->entry_co_ctx, sizeof(ngx_http_lua_co_ctx_t));\n\n    ctx->entry_co_ctx.next_zombie_child_thread =\n        &ctx->entry_co_ctx.zombie_child_threads;\n\n    ctx->entry_co_ctx.co_ref = LUA_NOREF;\n\n    ctx->entered_server_rewrite_phase = 0;\n    ctx->entered_rewrite_phase = 0;\n    ctx->entered_access_phase = 0;\n    ctx->entered_precontent_phase = 0;\n    ctx->entered_content_phase = 0;\n\n    ctx->exit_code = 0;\n    ctx->exited = 0;\n    ctx->resume_handler = ngx_http_lua_wev_handler;\n\n    ngx_str_null(&ctx->exec_uri);\n    ngx_str_null(&ctx->exec_args);\n\n    ctx->co_op = 0;\n}\n\n\n/* post read callback for rewrite and access phases */\nvoid\nngx_http_lua_generic_phase_post_read(ngx_http_request_t *r)\n{\n    ngx_http_lua_ctx_t  *ctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua post read for rewrite/access phases\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n    r->main->count--;\n\n    if (ctx == NULL) {\n        return;\n    }\n\n    ctx->read_body_done = 1;\n\n    if (ctx->waiting_more_body) {\n        ctx->waiting_more_body = 0;\n        ngx_http_core_run_phases(r);\n    }\n}\n\n\nvoid\nngx_http_lua_request_cleanup_handler(void *data)\n{\n    ngx_http_lua_ctx_t          *ctx = data;\n\n    ngx_http_lua_request_cleanup(ctx, 0 /* forcible */);\n}\n\n\nvoid\nngx_http_lua_request_cleanup(ngx_http_lua_ctx_t *ctx, int forcible)\n{\n    lua_State                   *L;\n    ngx_http_request_t          *r;\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    /*  force coroutine handling the request quit */\n    if (ctx == NULL) {\n        dd(\"ctx is NULL\");\n        return;\n    }\n\n    r = ctx->request;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua request cleanup: forcible=%d\", forcible);\n\n    if (ctx->cleanup) {\n        *ctx->cleanup = NULL;\n        ctx->cleanup = NULL;\n    }\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n#if 1\n    if (r->connection->fd == (ngx_socket_t) -1) {\n        /* being a fake request */\n\n        if (ctx->context == NGX_HTTP_LUA_CONTEXT_TIMER) {\n            /* being a timer handler */\n            lmcf->running_timers--;\n        }\n    }\n#endif\n\n    L = ngx_http_lua_get_lua_vm(r, ctx);\n\n    ngx_http_lua_finalize_threads(r, ctx, L);\n}\n\n\n/*\n * description:\n *  run a Lua coroutine specified by ctx->cur_co_ctx->co\n * return value:\n *  NGX_AGAIN:      I/O interruption: r->main->count intact\n *  NGX_DONE:       I/O interruption: r->main->count already incremented by 1\n *  NGX_ERROR:      error\n *  >= 200          HTTP status code\n */\nngx_int_t\nngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, volatile int nrets)\n{\n    ngx_http_lua_co_ctx_t   *next_coctx, *parent_coctx, *orig_coctx;\n    int                      rv, success = 1;\n    lua_State               *next_co;\n    lua_State               *old_co;\n    const char              *err, *msg, *trace;\n    ngx_int_t                rc;\n#if (NGX_PCRE)\n    ngx_pool_t              *old_pool = NULL;\n#endif\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run thread, top:%d c:%ud\", lua_gettop(L),\n                   r->main->count);\n\n    /* set Lua VM panic handler */\n    lua_atpanic(L, ngx_http_lua_atpanic);\n\n    NGX_LUA_EXCEPTION_TRY {\n\n        /*\n         * silence a -Werror=clobbered warning with gcc 5.4\n         * due to above setjmp\n         */\n        err = NULL;\n        msg = NULL;\n        trace = NULL;\n\n        if (ctx->cur_co_ctx->thread_spawn_yielded) {\n            ngx_http_lua_probe_info(\"thread spawn yielded\");\n\n            ctx->cur_co_ctx->thread_spawn_yielded = 0;\n            nrets = 1;\n        }\n\n        for ( ;; ) {\n\n            dd(\"ctx: %p, co: %p, co status: %d, co is_wrap: %d\",\n               ctx, ctx->cur_co_ctx->co, ctx->cur_co_ctx->co_status,\n               ctx->cur_co_ctx->is_wrap);\n\n#if (NGX_PCRE)\n            /* XXX: work-around to nginx regex subsystem */\n            old_pool = ngx_http_lua_pcre_malloc_init(r->pool);\n#endif\n\n            orig_coctx = ctx->cur_co_ctx;\n\n#ifdef NGX_LUA_USE_ASSERT\n            dd(\"%p: saved co top: %d, nrets: %d, true top: %d\",\n               orig_coctx->co,\n               (int) orig_coctx->co_top, (int) nrets,\n               (int) lua_gettop(orig_coctx->co));\n#endif\n\n#if DDEBUG\n            if (lua_gettop(orig_coctx->co) > 0) {\n                dd(\"co top elem: %s\", luaL_typename(orig_coctx->co, -1));\n            }\n\n            if (orig_coctx->propagate_error) {\n                dd(\"co propagate_error: %d\", orig_coctx->propagate_error);\n            }\n#endif\n\n            if (orig_coctx->propagate_error) {\n                orig_coctx->propagate_error = 0;\n                goto propagate_error;\n            }\n\n            ngx_http_lua_assert(orig_coctx->co_top + nrets\n                                == lua_gettop(orig_coctx->co));\n\n            rv = lua_resume(orig_coctx->co, nrets);\n\n#if (NGX_PCRE)\n            /* XXX: work-around to nginx regex subsystem */\n            ngx_http_lua_pcre_malloc_done(old_pool);\n#endif\n\n#if 0\n            /* test the longjmp thing */\n            if (rand() % 2 == 0) {\n                NGX_LUA_EXCEPTION_THROW(1);\n            }\n#endif\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"lua resume returned %d\", rv);\n\n            switch (rv) {\n            case LUA_YIELD:\n                /*  yielded, let event handler do the rest job */\n                /*  FIXME: add io cmd dispatcher here */\n\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"lua thread yielded\");\n\n#ifdef NGX_LUA_USE_ASSERT\n                dd(\"%p: saving curr top after yield: %d (co-op: %d)\",\n                   orig_coctx->co,\n                   (int) lua_gettop(orig_coctx->co), (int) ctx->co_op);\n                orig_coctx->co_top = lua_gettop(orig_coctx->co);\n#endif\n\n                if (r->uri_changed) {\n                    return ngx_http_lua_handle_rewrite_jump(L, r, ctx);\n                }\n\n                if (ctx->exited) {\n                    return ngx_http_lua_handle_exit(L, r, ctx);\n                }\n\n                if (ctx->exec_uri.len) {\n                    return ngx_http_lua_handle_exec(L, r, ctx);\n                }\n\n                /*\n                 * check if coroutine.resume or coroutine.yield called\n                 * lua_yield()\n                 */\n                switch (ctx->co_op) {\n\n                case NGX_HTTP_LUA_USER_CORO_NOP:\n                    dd(\"hit! it is the API yield\");\n\n                    ngx_http_lua_assert(lua_gettop(ctx->cur_co_ctx->co) == 0);\n\n                    ctx->cur_co_ctx = NULL;\n\n                    return NGX_AGAIN;\n\n                case NGX_HTTP_LUA_USER_THREAD_RESUME:\n\n                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                                   \"lua user thread resume\");\n\n                    ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP;\n                    nrets = lua_gettop(ctx->cur_co_ctx->co) - 1;\n                    dd(\"nrets = %d\", nrets);\n\n#ifdef NGX_LUA_USE_ASSERT\n                    /* ignore the return value (the thread) already pushed */\n                    orig_coctx->co_top--;\n#endif\n\n                    break;\n\n                case NGX_HTTP_LUA_USER_CORO_RESUME:\n                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                                   \"lua coroutine: resume\");\n\n                    /*\n                     * the target coroutine lies at the base of the\n                     * parent's stack\n                     */\n                    ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP;\n\n                    old_co = ctx->cur_co_ctx->parent_co_ctx->co;\n\n                    nrets = lua_gettop(old_co);\n                    if (nrets) {\n                        dd(\"moving %d return values to parent\", nrets);\n                        lua_xmove(old_co, ctx->cur_co_ctx->co, nrets);\n\n#ifdef NGX_LUA_USE_ASSERT\n                        ctx->cur_co_ctx->parent_co_ctx->co_top -= nrets;\n#endif\n                    }\n\n                    break;\n\n                default:\n                    /* ctx->co_op == NGX_HTTP_LUA_USER_CORO_YIELD */\n\n                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                                   \"lua coroutine: yield\");\n\n                    ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP;\n\n                    if (ngx_http_lua_is_thread(ctx)) {\n                        ngx_http_lua_probe_thread_yield(r, ctx->cur_co_ctx->co);\n\n                        /* discard any return values from user\n                         * coroutine.yield()'s arguments */\n                        lua_settop(ctx->cur_co_ctx->co, 0);\n\n#ifdef NGX_LUA_USE_ASSERT\n                        ctx->cur_co_ctx->co_top = 0;\n#endif\n\n                        ngx_http_lua_probe_info(\"set co running\");\n                        ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING;\n\n                        if (ctx->posted_threads) {\n                            ngx_http_lua_post_thread(r, ctx, ctx->cur_co_ctx);\n                            ctx->cur_co_ctx = NULL;\n                            return NGX_AGAIN;\n                        }\n\n                        /* no pending threads, so resume the thread\n                         * immediately */\n\n                        nrets = 0;\n                        continue;\n                    }\n\n                    /* being a user coroutine that has a parent */\n\n                    nrets = lua_gettop(ctx->cur_co_ctx->co);\n\n                    next_coctx = ctx->cur_co_ctx->parent_co_ctx;\n                    next_co = next_coctx->co;\n\n                    if (nrets) {\n                        dd(\"moving %d return values to next co\", nrets);\n                        lua_xmove(ctx->cur_co_ctx->co, next_co, nrets);\n#ifdef NGX_LUA_USE_ASSERT\n                        ctx->cur_co_ctx->co_top -= nrets;\n#endif\n                    }\n\n                    if (!ctx->cur_co_ctx->is_wrap) {\n                        /*\n                         * prepare return values for coroutine.resume\n                         * (true plus any retvals)\n                         */\n                        lua_pushboolean(next_co, 1);\n                        lua_insert(next_co, 1);\n                        nrets++;  /* add the true boolean value */\n                    }\n\n                    ctx->cur_co_ctx = next_coctx;\n\n                    break;\n                }\n\n                /* try resuming on the new coroutine again */\n                continue;\n\n            case 0:\n\n                ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);\n\n                ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1);\n\n                ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD;\n\n                if (ctx->cur_co_ctx->zombie_child_threads) {\n                    ngx_http_lua_cleanup_zombie_child_uthreads(r, L, ctx,\n                                                               ctx->cur_co_ctx);\n                }\n\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"lua light thread ended normally\");\n\n                if (ngx_http_lua_is_entry_thread(ctx)) {\n\n                    lua_settop(L, 0);\n\n                    ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);\n\n                    dd(\"uthreads: %d\", (int) ctx->uthreads);\n\n                    if (ctx->uthreads) {\n\n                        ctx->cur_co_ctx = NULL;\n                        return NGX_AGAIN;\n                    }\n\n                    /* all user threads terminated already */\n                    goto done;\n                }\n\n                if (ctx->cur_co_ctx->is_uthread) {\n                    /* being a user thread */\n\n                    lua_settop(L, 0);\n\n                    parent_coctx = ctx->cur_co_ctx->parent_co_ctx;\n\n                    if (ngx_http_lua_coroutine_alive(parent_coctx)) {\n                        if (ctx->cur_co_ctx->waited_by_parent) {\n                            ngx_http_lua_probe_info(\"parent already waiting\");\n                            ctx->cur_co_ctx->waited_by_parent = 0;\n                            success = 1;\n                            goto user_co_done;\n                        }\n\n                        ngx_http_lua_probe_info(\"parent still alive\");\n\n                        if (ngx_http_lua_post_zombie_thread(r, parent_coctx,\n                                                            ctx->cur_co_ctx)\n                            != NGX_OK)\n                        {\n                            return NGX_ERROR;\n                        }\n\n                        lua_pushboolean(ctx->cur_co_ctx->co, 1);\n                        lua_insert(ctx->cur_co_ctx->co, 1);\n\n                        ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_ZOMBIE;\n                        ctx->cur_co_ctx = NULL;\n                        return NGX_AGAIN;\n                    }\n\n                    if (ctx->cur_co_ctx->co_ref != LUA_NOREF) {\n                        ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);\n                        ctx->uthreads--;\n                    }\n\n                    if (ctx->uthreads == 0) {\n                        if (ngx_http_lua_entry_thread_alive(ctx)) {\n                            ctx->cur_co_ctx = NULL;\n                            return NGX_AGAIN;\n                        }\n\n                        /* all threads terminated already */\n                        goto done;\n                    }\n\n                    /* some other user threads still running */\n                    ctx->cur_co_ctx = NULL;\n                    return NGX_AGAIN;\n                }\n\n                /* being a user coroutine that has a parent */\n\n                success = 1;\n\nuser_co_done:\n\n                nrets = lua_gettop(ctx->cur_co_ctx->co);\n\n                next_coctx = ctx->cur_co_ctx->parent_co_ctx;\n\n                if (next_coctx == NULL) {\n                    /* being a light thread */\n                    goto no_parent;\n                }\n\n                next_co = next_coctx->co;\n\n                if (nrets) {\n                    lua_xmove(ctx->cur_co_ctx->co, next_co, nrets);\n                }\n\n                if (ctx->cur_co_ctx->is_uthread\n                    && ctx->cur_co_ctx->co_ref != LUA_NOREF)\n                {\n                    ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);\n                    ctx->uthreads--;\n                }\n\n                if (!ctx->cur_co_ctx->is_wrap) {\n                    /*\n                     * ended successfully, coroutine.resume returns true plus\n                     * any return values\n                     */\n                    lua_pushboolean(next_co, success);\n                    lua_insert(next_co, 1);\n                    nrets++;\n                }\n\n                ctx->cur_co_ctx = next_coctx;\n\n                ngx_http_lua_probe_info(\"set parent running\");\n\n                next_coctx->co_status = NGX_HTTP_LUA_CO_RUNNING;\n\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"lua coroutine: lua user thread ended normally\");\n\n                continue;\n\n            case LUA_ERRRUN:\n                err = \"runtime error\";\n                break;\n\n            case LUA_ERRSYNTAX:\n                err = \"syntax error\";\n                break;\n\n            case LUA_ERRMEM:\n                err = \"[lua] memory allocation error\";\n                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, err);\n                abort();\n                break;\n\n            case LUA_ERRERR:\n                err = \"error handler error\";\n                break;\n\n            default:\n                err = \"unknown error\";\n                break;\n            }\n\n            if (ctx->cur_co_ctx != orig_coctx) {\n                ctx->cur_co_ctx = orig_coctx;\n            }\n\n            ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);\n\n            ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 0);\n\n            ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD;\n\n            if (orig_coctx->is_uthread\n                || orig_coctx->is_wrap\n                || ngx_http_lua_is_entry_thread(ctx))\n            {\n                ngx_http_lua_thread_traceback(L, orig_coctx->co, orig_coctx);\n                trace = lua_tostring(L, -1);\n\n                if (lua_isstring(orig_coctx->co, -1)) {\n                    msg = lua_tostring(orig_coctx->co, -1);\n                    dd(\"user custom error msg: %s\", msg);\n\n                } else {\n                    msg = \"unknown reason\";\n                }\n            }\n\npropagate_error:\n\n            if (ctx->cur_co_ctx->is_uthread) {\n                ngx_http_lua_assert(err != NULL && msg != NULL\n                                    && trace != NULL);\n\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"lua user thread aborted: %s: %s\\n%s\",\n                              err, msg, trace);\n\n                lua_settop(L, 0);\n\n                parent_coctx = ctx->cur_co_ctx->parent_co_ctx;\n\n                if (ngx_http_lua_coroutine_alive(parent_coctx)) {\n                    if (ctx->cur_co_ctx->waited_by_parent) {\n                        ctx->cur_co_ctx->waited_by_parent = 0;\n                        success = 0;\n                        goto user_co_done;\n                    }\n\n                    if (ngx_http_lua_post_zombie_thread(r, parent_coctx,\n                                                        ctx->cur_co_ctx)\n                        != NGX_OK)\n                    {\n                        return NGX_ERROR;\n                    }\n\n                    lua_pushboolean(ctx->cur_co_ctx->co, 0);\n                    lua_insert(ctx->cur_co_ctx->co, 1);\n\n                    ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_ZOMBIE;\n                    ctx->cur_co_ctx = NULL;\n                    return NGX_AGAIN;\n                }\n\n                if (ctx->cur_co_ctx->co_ref != LUA_NOREF) {\n                    ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);\n                    ctx->uthreads--;\n                }\n\n                if (ctx->uthreads == 0) {\n                    if (ngx_http_lua_entry_thread_alive(ctx)) {\n                        ctx->cur_co_ctx = NULL;\n                        return NGX_AGAIN;\n                    }\n\n                    /* all threads terminated already */\n                    goto done;\n                }\n\n                /* some other user threads still running */\n                ctx->cur_co_ctx = NULL;\n                return NGX_AGAIN;\n            }\n\n            if (ngx_http_lua_is_entry_thread(ctx)) {\n                ngx_http_lua_assert(err != NULL && msg != NULL\n                                    && trace != NULL);\n\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"lua entry thread aborted: %s: %s\\n%s\",\n                              err, msg, trace);\n\n                lua_settop(L, 0);\n\n                /* being the entry thread aborted */\n\n                if (r->filter_finalize) {\n                    ngx_http_set_ctx(r, ctx, ngx_http_lua_module);\n                }\n\n                ngx_http_lua_request_cleanup(ctx, 0);\n\n                dd(\"headers sent? %d\", r->header_sent || ctx->header_sent);\n\n                if (ctx->no_abort) {\n                    ctx->no_abort = 0;\n                    return NGX_ERROR;\n                }\n\n                return (r->header_sent || ctx->header_sent) ? NGX_ERROR :\n                       NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            /* being a user coroutine that has a parent */\n\n            next_coctx = ctx->cur_co_ctx->parent_co_ctx;\n            if (next_coctx == NULL) {\n                goto no_parent;\n            }\n\n            next_co = next_coctx->co;\n\n            ngx_http_lua_probe_info(\"set parent running\");\n\n            next_coctx->co_status = NGX_HTTP_LUA_CO_RUNNING;\n\n            ctx->cur_co_ctx = next_coctx;\n\n            if (orig_coctx->is_wrap) {\n                /*\n                 * coroutine.wrap propagates errors\n                 * to its parent coroutine\n                 */\n                next_coctx->propagate_error = 1;\n                continue;\n            }\n\n            /*\n             * ended with error, coroutine.resume returns false plus\n             * err msg\n             */\n            lua_pushboolean(next_co, 0);\n            lua_xmove(orig_coctx->co, next_co, 1);\n            nrets = 2;\n\n            /* try resuming on the new coroutine again */\n            continue;\n        }\n\n    } NGX_LUA_EXCEPTION_CATCH {\n        dd(\"nginx execution restored\");\n    }\n\n    return NGX_ERROR;\n\nno_parent:\n\n    lua_settop(L, 0);\n\n    ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD;\n\n    if (r->filter_finalize) {\n        ngx_http_set_ctx(r, ctx, ngx_http_lua_module);\n    }\n\n    ngx_http_lua_request_cleanup(ctx, 0);\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \"lua handler aborted: \"\n                  \"user coroutine has no parent\");\n\n    return (r->header_sent || ctx->header_sent) ?\n                NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR;\n\ndone:\n\n#if HAVE_LUA_PROXY_SSL\n    if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT\n        || ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY)\n    {\n        return NGX_OK;\n    }\n#endif\n\n    if (ctx->entered_content_phase\n        && r->connection->fd != (ngx_socket_t) -1)\n    {\n        rc = ngx_http_lua_send_chain_link(r, ctx,\n                                          NULL /* last_buf */);\n\n        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_lua_wev_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                    rc;\n    ngx_event_t                 *wev;\n    ngx_connection_t            *c;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_core_loc_conf_t    *clcf;\n\n    ngx_http_lua_socket_tcp_upstream_t *u;\n\n    c = r->connection;\n    wev = c->write;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"lua run write event handler: timedout:%ud, ready:%ud, \"\n                   \"writing_raw_req_socket:%ud\",\n                   wev->timedout, wev->ready, ctx->writing_raw_req_socket);\n\n    clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);\n\n    if (wev->timedout && !ctx->writing_raw_req_socket) {\n        if (!wev->delayed) {\n            ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,\n                          \"client timed out\");\n            c->timedout = 1;\n\n            goto flush_coros;\n        }\n\n        wev->timedout = 0;\n        wev->delayed = 0;\n\n        if (!wev->ready) {\n            ngx_add_timer(wev, clcf->send_timeout);\n\n            if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {\n                if (ctx->entered_content_phase) {\n                    ngx_http_lua_finalize_request(r, NGX_ERROR);\n                }\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    if (!wev->ready && !wev->timedout) {\n        goto useless;\n    }\n\n    if (ctx->writing_raw_req_socket) {\n        ctx->writing_raw_req_socket = 0;\n\n        u = ctx->downstream;\n        if (u == NULL) {\n            return NGX_ERROR;\n        }\n\n        u->write_event_handler(r, u);\n        return NGX_DONE;\n    }\n\n    if (c->buffered & (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED)) {\n        rc = ngx_http_lua_flush_pending_output(r, ctx);\n\n        dd(\"flush pending output returned %d, c->error: %d\", (int) rc,\n           c->error);\n\n        if (rc != NGX_ERROR && rc != NGX_OK) {\n            goto useless;\n        }\n\n        /* when rc == NGX_ERROR, c->error must be set */\n    }\n\nflush_coros:\n\n    dd(\"ctx->flushing_coros: %d\", (int) ctx->flushing_coros);\n\n    if (ctx->flushing_coros) {\n        return ngx_http_lua_process_flushing_coroutines(r, ctx);\n    }\n\n    /* ctx->flushing_coros == 0 */\n\nuseless:\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"useless lua write event handler\");\n\n    if (ctx->entered_content_phase) {\n        return NGX_OK;\n    }\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_process_flushing_coroutines(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx)\n{\n    ngx_int_t                    rc, n;\n    ngx_uint_t                   i;\n    ngx_list_part_t             *part;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    dd(\"processing flushing coroutines\");\n\n    coctx = &ctx->entry_co_ctx;\n    n = ctx->flushing_coros;\n\n    if (coctx->flushing) {\n        coctx->flushing = 0;\n\n        ctx->flushing_coros--;\n        n--;\n        ctx->cur_co_ctx = coctx;\n\n        rc = ngx_http_lua_flush_resume_helper(r, ctx);\n        if (rc == NGX_ERROR || rc >= NGX_OK) {\n            return rc;\n        }\n\n        /* rc == NGX_DONE */\n    }\n\n    if (n) {\n\n        if (ctx->user_co_ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        part = &ctx->user_co_ctx->part;\n        coctx = part->elts;\n\n        for (i = 0; /* void */; i++) {\n\n            if (i >= part->nelts) {\n                if (part->next == NULL) {\n                    break;\n                }\n\n                part = part->next;\n                coctx = part->elts;\n                i = 0;\n            }\n\n            if (coctx[i].flushing) {\n                coctx[i].flushing = 0;\n                ctx->flushing_coros--;\n                n--;\n                ctx->cur_co_ctx = &coctx[i];\n\n                rc = ngx_http_lua_flush_resume_helper(r, ctx);\n                if (rc == NGX_ERROR || rc >= NGX_OK) {\n                    return rc;\n                }\n\n                /* rc == NGX_DONE */\n\n                if (n == 0) {\n                    return NGX_DONE;\n                }\n            }\n        }\n    }\n\n    if (n) {\n        return NGX_ERROR;\n    }\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_flush_pending_output(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx)\n{\n    ngx_int_t           rc;\n    ngx_chain_t        *cl;\n    ngx_event_t        *wev;\n    ngx_connection_t   *c;\n\n    ngx_http_core_loc_conf_t    *clcf;\n\n    c = r->connection;\n    wev = c->write;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"lua flushing output: buffered 0x%uxd\",\n                   c->buffered);\n\n    if (ctx->busy_bufs) {\n        /* FIXME since cosockets also share this busy_bufs chain, this condition\n         * might not be strong enough. better use separate busy_bufs chains. */\n        rc = ngx_http_lua_output_filter(r, NULL);\n\n    } else {\n        cl = ngx_http_lua_get_flush_chain(r, ctx);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        rc = ngx_http_lua_output_filter(r, cl);\n    }\n\n    dd(\"output filter returned %d\", (int) rc);\n\n    if (rc == NGX_ERROR || rc > NGX_OK) {\n        return rc;\n    }\n\n    if (c->buffered & (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED)) {\n\n        clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);\n\n        if (!wev->delayed) {\n            ngx_add_timer(wev, clcf->send_timeout);\n        }\n\n        if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {\n            if (ctx->entered_content_phase) {\n                ngx_http_lua_finalize_request(r, NGX_ERROR);\n            }\n\n            return NGX_ERROR;\n        }\n\n        if (ctx->flushing_coros) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"lua flush still waiting: buffered 0x%uxd\",\n                           c->buffered);\n\n            return NGX_DONE;\n        }\n\n    } else {\n#if 1\n        if (wev->timer_set && !wev->delayed) {\n            ngx_del_timer(wev);\n        }\n#endif\n    }\n\n    return NGX_OK;\n}\n\n\nu_char *\nngx_http_lua_digest_hex(u_char *dest, const u_char *buf, int buf_len)\n{\n    ngx_md5_t                     md5;\n    u_char                        md5_buf[MD5_DIGEST_LENGTH];\n\n    ngx_md5_init(&md5);\n    ngx_md5_update(&md5, buf, buf_len);\n    ngx_md5_final(md5_buf, &md5);\n\n    return ngx_hex_dump(dest, md5_buf, sizeof(md5_buf));\n}\n\n\nvoid\nngx_http_lua_set_multi_value_table(lua_State *L, int index)\n{\n    if (index < 0) {\n        index = lua_gettop(L) + index + 1;\n    }\n\n    lua_pushvalue(L, -2); /* stack: table key value key */\n    lua_rawget(L, index);\n    if (lua_isnil(L, -1)) {\n        lua_pop(L, 1); /* stack: table key value */\n        lua_rawset(L, index); /* stack: table */\n\n    } else {\n        if (!lua_istable(L, -1)) {\n            /* just inserted one value */\n            lua_createtable(L, 4, 0);\n                /* stack: table key value value table */\n            lua_insert(L, -2);\n                /* stack: table key value table value */\n            lua_rawseti(L, -2, 1);\n                /* stack: table key value table */\n            lua_insert(L, -2);\n                /* stack: table key table value */\n\n            lua_rawseti(L, -2, 2); /* stack: table key table */\n\n            lua_rawset(L, index); /* stack: table */\n\n        } else {\n            /* stack: table key value table */\n            lua_insert(L, -2); /* stack: table key table value */\n\n            lua_rawseti(L, -2, lua_objlen(L, -2) + 1);\n                /* stack: table key table  */\n            lua_pop(L, 2); /* stack: table */\n        }\n    }\n}\n\n\nuintptr_t\nngx_http_lua_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)\n{\n    ngx_uint_t      n;\n    uint32_t       *escape;\n    static u_char   hex[] = \"0123456789ABCDEF\";\n\n                    /* \" \", \"#\", \"%\", \"?\", %00-%1F, %7F-%FF */\n\n    static uint32_t   uri[] = {\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0x80000029, /* 1000 0000 0000 0000  0000 0000 0010 1001 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */\n\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n    };\n\n                    /* \" \", \"#\", \"%\", \"+\", \"?\", %00-%1F, %7F-%FF */\n\n    static uint32_t   args[] = {\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0x80000829, /* 1000 0000 0000 0000  0000 1000 0010 1001 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */\n\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n    };\n\n                    /* not ALPHA, DIGIT, \"-\", \".\", \"_\", \"~\" */\n\n    static uint32_t   uri_component[] = {\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0xfc00987d, /* 1111 1100 0000 0000  1001 1000 0111 1101 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x78000001, /* 0111 1000 0000 0000  0000 0000 0000 0001 */\n\n                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n        0xb8000001, /* 1011 1000 0000 0000  0000 0000 0000 0001 */\n\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n    };\n\n                    /* \" \", \"#\", \"\"\", \"%\", \"'\", %00-%1F, %7F-%FF */\n\n    static uint32_t   html[] = {\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0x000000ad, /* 0000 0000 0000 0000  0000 0000 1010 1101 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */\n\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n    };\n\n                    /* \" \", \"\"\", \"%\", \"'\", %00-%1F, %7F-%FF */\n\n    static uint32_t   refresh[] = {\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0x00000085, /* 0000 0000 0000 0000  0000 0000 1000 0101 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */\n\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n    };\n\n                    /* \" \", \"%\", %00-%1F */\n\n    static uint32_t   memcached[] = {\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0x00000021, /* 0000 0000 0000 0000  0000 0000 0010 0001 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n    };\n\n                    /* mail_auth is the same as memcached */\n\n                    /* \" \", \"\"\", \"(\", \")\", \",\", \"/\", \":\", \";\", \"?\",\n                     * \"<\", \"=\", \">\", \"?\", \"@\", \"[\", \"]\", \"\\\", \"{\",\n                     * \"}\", %00-%1F, %7F-%FF\n                     */\n\n    static uint32_t   header_name[] = {\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0xfc009305, /* 1111 1100 0000 0000  1001 0011 0000 0101 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x38000001, /* 0011 1000 0000 0000  0000 0000 0000 0001 */\n\n                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n        0xa8000000, /* 1010 1000 0000 0000  0000 0000 0000 0000 */\n\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n    };\n\n                    /* \"%00-%08, %0A-%0F, %7F */\n\n    static uint32_t   header_value[] = {\n        0xfffffdff, /* 1111 1111 1111 1111  1111 1101 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */\n\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n    };\n\n    static uint32_t  *map[] =\n        { uri, args, uri_component, html, refresh, memcached, memcached,\n          header_name, header_value };\n\n    escape = map[type];\n\n    if (dst == NULL) {\n\n        /* find the number of the characters to be escaped */\n\n        n = 0;\n\n        while (size) {\n            if (escape[*src >> 5] & (1 << (*src & 0x1f))) {\n                n++;\n            }\n\n            src++;\n            size--;\n        }\n\n        return (uintptr_t) n;\n    }\n\n    while (size) {\n        if (escape[*src >> 5] & (1 << (*src & 0x1f))) {\n            *dst++ = '%';\n            *dst++ = hex[*src >> 4];\n            *dst++ = hex[*src & 0xf];\n            src++;\n\n        } else {\n            *dst++ = *src++;\n        }\n\n        size--;\n    }\n\n    return (uintptr_t) dst;\n}\n\n\nstatic int\nngx_http_lua_util_hex2int(char xdigit)\n{\n    if (isdigit(xdigit)) {\n        return xdigit - '0';\n    }\n\n    xdigit = tolower(xdigit);\n    if (xdigit <= 'f' && xdigit >= 'a') {\n        return xdigit - 'a' + 10;\n    }\n\n    return -1;\n}\n\n\n/* XXX we also decode '+' to ' ' */\nvoid\nngx_http_lua_unescape_uri(u_char **dst, u_char **src, size_t size,\n    ngx_uint_t type)\n{\n    u_char *d = *dst, *s = *src, *de = (*dst + size);\n    int     isuri = type & NGX_UNESCAPE_URI;\n    int     isredirect = type & NGX_UNESCAPE_REDIRECT;\n\n    while (size--) {\n        u_char curr = *s++;\n\n        if (curr == '?' &&\n            (type & (NGX_UNESCAPE_URI | NGX_UNESCAPE_REDIRECT)))\n        {\n            *d++ = '?';\n            break;\n\n        } else if (curr == '%') {\n            u_char ch;\n            if (size < 2 || !(isxdigit(s[0]) && isxdigit(s[1]))) {\n                *d++ = '%';\n                continue;\n            }\n            /* we can be sure here they must be hex digits */\n            ch = ngx_http_lua_util_hex2int(s[0]) * 16 +\n                 ngx_http_lua_util_hex2int(s[1]);\n\n            if ((isuri || isredirect) && ch == '?') {\n                *d++ = ch;\n                break;\n\n            } else if (isredirect && (ch <= '%' || ch >= 0x7f)) {\n                *d++ = '%';\n                continue;\n            }\n\n            *d++ = ch;\n            s += 2;\n            size -= 2;\n\n        } else if (curr == '+') {\n            *d++ = ' ';\n            continue;\n\n        } else {\n            *d++ = curr;\n        }\n    }\n\n    /* a safe guard if dst need to be null-terminated */\n    if (d != de) {\n        *d = '\\0';\n    }\n\n    *dst = d;\n    *src = s;\n}\n\n\nvoid\nngx_http_lua_inject_req_api(ngx_log_t *log, lua_State *L)\n{\n    /* ngx.req table */\n\n    lua_createtable(L, 0 /* narr */, 23 /* nrec */);    /* .req */\n\n    ngx_http_lua_inject_req_header_api(L);\n    ngx_http_lua_inject_req_uri_api(log, L);\n    ngx_http_lua_inject_req_args_api(L);\n    ngx_http_lua_inject_req_body_api(L);\n    ngx_http_lua_inject_req_socket_api(L);\n    ngx_http_lua_inject_req_misc_api(L);\n\n    lua_setfield(L, -2, \"req\");\n}\n\n\nstatic ngx_int_t\nngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx)\n{\n    ngx_int_t               rc;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua thread initiated internal redirect to %V\",\n                   &ctx->exec_uri);\n\n    ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);\n\n    ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1);\n\n    ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD;\n\n    if (r->filter_finalize) {\n        ngx_http_set_ctx(r, ctx, ngx_http_lua_module);\n    }\n\n    ngx_http_lua_request_cleanup(ctx, 1 /* forcible */);\n\n    if (ctx->exec_uri.data[0] == '@') {\n        if (ctx->exec_args.len > 0) {\n            ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                          \"query strings %V ignored when exec'ing \"\n                          \"named location %V\",\n                          &ctx->exec_args, &ctx->exec_uri);\n        }\n\n        r->write_event_handler = ngx_http_request_empty_handler;\n\n#if 1\n        if (r->read_event_handler == ngx_http_lua_rd_check_broken_connection) {\n            /* resume the read event handler */\n\n            r->read_event_handler = ngx_http_block_reading;\n        }\n#endif\n\n#if 1\n        /* clear the modules contexts */\n        ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);\n#endif\n\n        rc = ngx_http_named_location(r, &ctx->exec_uri);\n        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n\n#if 0\n        if (!ctx->entered_content_phase) {\n            /* XXX ensure the main request ref count\n             * is decreased because the current\n             * request will be quit */\n            r->main->count--;\n            dd(\"XXX decrement main count: c:%d\", (int) r->main->count);\n        }\n#endif\n\n        return NGX_DONE;\n    }\n\n    dd(\"internal redirect to %.*s\", (int) ctx->exec_uri.len,\n       ctx->exec_uri.data);\n\n    r->write_event_handler = ngx_http_request_empty_handler;\n\n    if (r->read_event_handler == ngx_http_lua_rd_check_broken_connection) {\n        /* resume the read event handler */\n\n        r->read_event_handler = ngx_http_block_reading;\n    }\n\n    rc = ngx_http_internal_redirect(r, &ctx->exec_uri, &ctx->exec_args);\n\n    dd(\"internal redirect returned %d when in content phase? \"\n       \"%d\", (int) rc, ctx->entered_content_phase);\n\n    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        return rc;\n    }\n\n#if 0\n    if (!ctx->entered_content_phase) {\n        /* XXX ensure the main request ref count\n         * is decreased because the current\n         * request will be quit */\n        dd(\"XXX decrement main count\");\n        r->main->count--;\n    }\n#endif\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_handle_exit(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx)\n{\n    ngx_int_t           rc;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua thread aborting request with status %d\",\n                   ctx->exit_code);\n\n    ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);\n\n    ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1);\n\n    ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD;\n\n    if (r->filter_finalize) {\n        ngx_http_set_ctx(r, ctx, ngx_http_lua_module);\n    }\n\n    ngx_http_lua_request_cleanup(ctx, 0);\n\n    if (r->connection->fd == (ngx_socket_t) -1) {  /* fake request */\n        return ctx->exit_code;\n    }\n\n#if HAVE_LUA_PROXY_SSL\n    if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT\n        || ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY)\n    {\n        return ctx->exit_code;\n    }\n#endif\n\n#if 1\n    if (!r->header_sent\n        && !ctx->header_sent\n        && r->headers_out.status == 0\n        && ctx->exit_code >= NGX_HTTP_OK)\n    {\n        r->headers_out.status = ctx->exit_code;\n    }\n#endif\n\n    if (ctx->buffering\n        && r->headers_out.status\n        && ctx->exit_code != NGX_ERROR\n        && ctx->exit_code != NGX_HTTP_REQUEST_TIME_OUT\n        && ctx->exit_code != NGX_HTTP_CLIENT_CLOSED_REQUEST\n        && ctx->exit_code != NGX_HTTP_CLOSE)\n    {\n        rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */);\n\n        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n\n        if (ctx->exit_code >= NGX_HTTP_OK) {\n            return NGX_HTTP_OK;\n        }\n\n        return ctx->exit_code;\n    }\n\n    if ((ctx->exit_code == NGX_OK\n         && ctx->entered_content_phase)\n        || (ctx->exit_code >= NGX_HTTP_OK\n            && ctx->exit_code < NGX_HTTP_SPECIAL_RESPONSE\n            && ctx->exit_code != NGX_HTTP_NO_CONTENT))\n    {\n        rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */);\n\n        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n    }\n\n#if 1\n    if ((r->header_sent || ctx->header_sent)\n        && ctx->exit_code > NGX_OK\n        && ctx->exit_code != NGX_HTTP_REQUEST_TIME_OUT\n        && ctx->exit_code != NGX_HTTP_CLIENT_CLOSED_REQUEST\n        && ctx->exit_code != NGX_HTTP_CLOSE)\n    {\n        if (ctx->entered_content_phase) {\n            return NGX_OK;\n        }\n\n        return NGX_HTTP_OK;\n    }\n#endif\n\n    return ctx->exit_code;\n}\n\n\nvoid\nngx_http_lua_process_args_option(ngx_http_request_t *r, lua_State *L,\n    int table, ngx_str_t *args)\n{\n    u_char              *key;\n    size_t               key_len;\n    u_char              *value;\n    size_t               value_len;\n    size_t               len = 0;\n    size_t               key_escape = 0;\n    uintptr_t            total_escape = 0;\n    int                  n;\n    int                  i;\n    u_char              *p;\n\n    if (table < 0) {\n        table = lua_gettop(L) + table + 1;\n    }\n\n    n = 0;\n    lua_pushnil(L);\n    while (lua_next(L, table) != 0) {\n        if (lua_type(L, -2) != LUA_TSTRING) {\n            luaL_error(L, \"attempt to use a non-string key in the \"\n                       \"\\\"args\\\" option table\");\n            return;\n        }\n\n        key = (u_char *) lua_tolstring(L, -2, &key_len);\n\n        key_escape = 2 * ngx_http_lua_escape_uri(NULL, key, key_len,\n                                                 NGX_ESCAPE_URI_COMPONENT);\n        total_escape += key_escape;\n\n        switch (lua_type(L, -1)) {\n        case LUA_TNUMBER:\n        case LUA_TSTRING:\n            value = (u_char *) lua_tolstring(L, -1, &value_len);\n\n            total_escape += 2 * ngx_http_lua_escape_uri(NULL, value, value_len,\n                                                      NGX_ESCAPE_URI_COMPONENT);\n\n            len += key_len + value_len + (sizeof(\"=\") - 1);\n            n++;\n\n            break;\n\n        case LUA_TBOOLEAN:\n            if (lua_toboolean(L, -1)) {\n                len += key_len;\n                n++;\n            }\n\n            break;\n\n        case LUA_TTABLE:\n\n            i = 0;\n            lua_pushnil(L);\n            while (lua_next(L, -2) != 0) {\n                if (lua_isboolean(L, -1)) {\n                    if (lua_toboolean(L, -1)) {\n                        len += key_len;\n\n                    } else {\n                        lua_pop(L, 1);\n                        continue;\n                    }\n\n                } else {\n                    value = (u_char *) lua_tolstring(L, -1, &value_len);\n\n                    if (value == NULL) {\n                        luaL_error(L, \"attempt to use %s as query arg value\",\n                                   luaL_typename(L, -1));\n                        return;\n                    }\n\n                    total_escape +=\n                        2 * ngx_http_lua_escape_uri(NULL, value,\n                                                    value_len,\n                                                    NGX_ESCAPE_URI_COMPONENT);\n\n                    len += key_len + value_len + (sizeof(\"=\") - 1);\n                }\n\n                if (i++ > 0) {\n                    total_escape += key_escape;\n                }\n\n                n++;\n                lua_pop(L, 1);\n            }\n\n            break;\n\n        default:\n            luaL_error(L, \"attempt to use %s as query arg value\",\n                       luaL_typename(L, -1));\n            return;\n        }\n\n        lua_pop(L, 1);\n    }\n\n    len += (size_t) total_escape;\n\n    if (n > 1) {\n        len += (n - 1) * (sizeof(\"&\") - 1);\n    }\n\n    dd(\"len 1: %d\", (int) len);\n\n    if (r) {\n        p = ngx_palloc(r->pool, len);\n        if (p == NULL) {\n            luaL_error(L, \"no memory\");\n            return;\n        }\n\n    } else {\n        p = lua_newuserdata(L, len);\n    }\n\n    args->data = p;\n    args->len = len;\n\n    i = 0;\n    lua_pushnil(L);\n    while (lua_next(L, table) != 0) {\n        key = (u_char *) lua_tolstring(L, -2, &key_len);\n\n        switch (lua_type(L, -1)) {\n        case LUA_TNUMBER:\n        case LUA_TSTRING:\n\n            if (total_escape) {\n                p = (u_char *) ngx_http_lua_escape_uri(p, key, key_len,\n                                                       NGX_ESCAPE_URI_COMPONENT\n                                                       );\n\n            } else {\n                dd(\"shortcut: no escape required\");\n\n                p = ngx_copy(p, key, key_len);\n            }\n\n            *p++ = '=';\n\n            value = (u_char *) lua_tolstring(L, -1, &value_len);\n\n            if (total_escape) {\n                p = (u_char *) ngx_http_lua_escape_uri(p, value, value_len,\n                                                       NGX_ESCAPE_URI_COMPONENT\n                                                       );\n\n            } else {\n                p = ngx_copy(p, value, value_len);\n            }\n\n            if (i != n - 1) {\n                /* not the last pair */\n                *p++ = '&';\n            }\n\n            i++;\n\n            break;\n\n        case LUA_TBOOLEAN:\n            if (lua_toboolean(L, -1)) {\n                if (total_escape) {\n                    p = (u_char *) ngx_http_lua_escape_uri(p, key, key_len,\n                                                NGX_ESCAPE_URI_COMPONENT);\n\n                } else {\n                    dd(\"shortcut: no escape required\");\n\n                    p = ngx_copy(p, key, key_len);\n                }\n\n                if (i != n - 1) {\n                    /* not the last pair */\n                    *p++ = '&';\n                }\n\n                i++;\n            }\n\n            break;\n\n        case LUA_TTABLE:\n\n            lua_pushnil(L);\n            while (lua_next(L, -2) != 0) {\n\n                if (lua_isboolean(L, -1)) {\n                    if (lua_toboolean(L, -1)) {\n                        if (total_escape) {\n                            p = (u_char *)\n                                    ngx_http_lua_escape_uri(p, key, key_len,\n                                                      NGX_ESCAPE_URI_COMPONENT);\n\n                        } else {\n                            dd(\"shortcut: no escape required\");\n\n                            p = ngx_copy(p, key, key_len);\n                        }\n\n                    } else {\n                        lua_pop(L, 1);\n                        continue;\n                    }\n\n                } else {\n\n                    if (total_escape) {\n                        p = (u_char *)\n                                ngx_http_lua_escape_uri(p, key,\n                                                        key_len,\n                                                        NGX_ESCAPE_URI_COMPONENT\n                                                        );\n\n                    } else {\n                        dd(\"shortcut: no escape required\");\n\n                        p = ngx_copy(p, key, key_len);\n                    }\n\n                    *p++ = '=';\n\n                    value = (u_char *) lua_tolstring(L, -1, &value_len);\n\n                    if (total_escape) {\n                        p = (u_char *)\n                                ngx_http_lua_escape_uri(p, value,\n                                                        value_len,\n                                                        NGX_ESCAPE_URI_COMPONENT\n                                                        );\n\n                    } else {\n                        p = ngx_copy(p, value, value_len);\n                    }\n                }\n\n                if (i != n - 1) {\n                    /* not the last pair */\n                    *p++ = '&';\n                }\n\n                i++;\n                lua_pop(L, 1);\n            }\n\n            break;\n\n        default:\n            luaL_error(L, \"should not reach here\");\n            return;\n        }\n\n        lua_pop(L, 1);\n    }\n\n    if (p - args->data != (ssize_t) len) {\n        luaL_error(L, \"buffer error: %d != %d\",\n                   (int) (p - args->data), (int) len);\n        return;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_lua_handle_rewrite_jump(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx)\n{\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua thread aborting request with URI rewrite jump: \"\n                   \"\\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);\n\n    ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1);\n\n    ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD;\n\n    if (r->filter_finalize) {\n        ngx_http_set_ctx(r, ctx, ngx_http_lua_module);\n    }\n\n    ngx_http_lua_request_cleanup(ctx, 1 /* forcible */);\n    ngx_http_lua_init_ctx(r, ctx);\n\n    return NGX_OK;\n}\n\n\n/* XXX ngx_open_and_stat_file is static in the core. sigh. */\nngx_int_t\nngx_http_lua_open_and_stat_file(u_char *name, ngx_open_file_info_t *of,\n    ngx_log_t *log)\n{\n    ngx_fd_t         fd;\n    ngx_file_info_t  fi;\n\n    if (of->fd != NGX_INVALID_FILE) {\n\n        if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {\n            of->failed = ngx_file_info_n;\n            goto failed;\n        }\n\n        if (of->uniq == ngx_file_uniq(&fi)) {\n            goto done;\n        }\n\n    } else if (of->test_dir) {\n\n        if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {\n            of->failed = ngx_file_info_n;\n            goto failed;\n        }\n\n        if (ngx_is_dir(&fi)) {\n            goto done;\n        }\n    }\n\n    if (!of->log) {\n\n        /*\n         * Use non-blocking open() not to hang on FIFO files, etc.\n         * This flag has no effect on a regular files.\n         */\n\n        fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,\n                           NGX_FILE_OPEN, 0);\n\n    } else {\n        fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN,\n                           NGX_FILE_DEFAULT_ACCESS);\n    }\n\n    if (fd == NGX_INVALID_FILE) {\n        of->failed = ngx_open_file_n;\n        goto failed;\n    }\n\n    if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,\n                      ngx_fd_info_n \" \\\"%s\\\" failed\", name);\n\n        if (ngx_close_file(fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                          ngx_close_file_n \" \\\"%s\\\" failed\", name);\n        }\n\n        of->fd = NGX_INVALID_FILE;\n\n        return NGX_ERROR;\n    }\n\n    if (ngx_is_dir(&fi)) {\n        if (ngx_close_file(fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                          ngx_close_file_n \" \\\"%s\\\" failed\", name);\n        }\n\n        of->fd = NGX_INVALID_FILE;\n\n    } else {\n        of->fd = fd;\n\n        if (of->directio <= ngx_file_size(&fi)) {\n            if (ngx_directio_on(fd) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                              ngx_directio_on_n \" \\\"%s\\\" failed\", name);\n\n            } else {\n                of->is_directio = 1;\n            }\n        }\n    }\n\ndone:\n\n    of->uniq = ngx_file_uniq(&fi);\n    of->mtime = ngx_file_mtime(&fi);\n    of->size = ngx_file_size(&fi);\n    of->fs_size = ngx_file_fs_size(&fi);\n    of->is_dir = ngx_is_dir(&fi);\n    of->is_file = ngx_is_file(&fi);\n    of->is_link = ngx_is_link(&fi);\n    of->is_exec = ngx_is_exec(&fi);\n\n    return NGX_OK;\n\nfailed:\n\n    of->fd = NGX_INVALID_FILE;\n    of->err = ngx_errno;\n\n    return NGX_ERROR;\n}\n\n\nngx_chain_t *\nngx_http_lua_chain_get_free_buf(ngx_log_t *log, ngx_pool_t *p,\n    ngx_chain_t **free, size_t len)\n{\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl;\n    u_char       *start, *end;\n\n    const ngx_buf_tag_t  tag = (ngx_buf_tag_t) &ngx_http_lua_module;\n\n    if (*free) {\n        cl = *free;\n        *free = cl->next;\n        cl->next = NULL;\n\n        b = cl->buf;\n        start = b->start;\n        end = b->end;\n        if (start && (size_t) (end - start) >= len) {\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0,\n                           \"lua reuse free buf memory %O >= %uz, cl:%p, p:%p\",\n                           (off_t) (end - start), len, cl, start);\n\n            ngx_memzero(b, sizeof(ngx_buf_t));\n\n            b->start = start;\n            b->pos = start;\n            b->last = start;\n            b->end = end;\n            b->tag = tag;\n\n            if (len) {\n                b->temporary = 1;\n            }\n\n            return cl;\n        }\n\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0,\n                       \"lua reuse free buf chain, but reallocate memory \"\n                       \"because %uz >= %O, cl:%p, p:%p\", len,\n                       (off_t) (b->end - b->start), cl, b->start);\n\n        if (ngx_buf_in_memory(b) && b->start) {\n            ngx_pfree(p, b->start);\n        }\n\n        ngx_memzero(b, sizeof(ngx_buf_t));\n\n        if (len == 0) {\n            return cl;\n        }\n\n        b->start = ngx_palloc(p, len);\n        if (b->start == NULL) {\n            return NULL;\n        }\n\n        b->end = b->start + len;\n\n        dd(\"buf start: %p\", cl->buf->start);\n\n        b->pos = b->start;\n        b->last = b->start;\n        b->tag = tag;\n        b->temporary = 1;\n\n        return cl;\n    }\n\n    cl = ngx_alloc_chain_link(p);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"lua allocate new chainlink and new buf of size %uz, cl:%p\",\n                   len, cl);\n\n    cl->buf = len ? ngx_create_temp_buf(p, len) : ngx_calloc_buf(p);\n    if (cl->buf == NULL) {\n        return NULL;\n    }\n\n    dd(\"buf start: %p\", cl->buf->start);\n\n    cl->buf->tag = tag;\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nstatic int\nngx_http_lua_thread_traceback(lua_State *L, lua_State *co,\n    ngx_http_lua_co_ctx_t *coctx)\n{\n    int         base;\n    int         level, coid;\n    lua_Debug   ar;\n\n    base = lua_gettop(L);\n    lua_checkstack(L, 3);\n    lua_pushliteral(L, \"stack traceback:\");\n    coid = 0;\n\n    while (co) {\n\n        if (coid >= NGX_HTTP_LUA_BT_MAX_COROS) {\n            break;\n        }\n\n        lua_checkstack(L, 2);\n        lua_pushfstring(L, \"\\ncoroutine %d:\", coid++);\n\n        level = 0;\n\n        while (lua_getstack(co, level++, &ar)) {\n\n            lua_checkstack(L, 5);\n\n            if (level > NGX_HTTP_LUA_BT_DEPTH) {\n                lua_pushliteral(L, \"\\n\\t...\");\n                break;\n            }\n\n            lua_pushliteral(L, \"\\n\\t\");\n            lua_getinfo(co, \"Snl\", &ar);\n            lua_pushfstring(L, \"%s:\", ar.short_src);\n\n            if (ar.currentline > 0) {\n                lua_pushfstring(L, \"%d:\", ar.currentline);\n            }\n\n            if (*ar.namewhat != '\\0') {  /* is there a name? */\n                lua_pushfstring(L, \" in function \" LUA_QS, ar.name);\n\n            } else {\n                if (*ar.what == 'm') {  /* main? */\n                    lua_pushliteral(L, \" in main chunk\");\n\n                } else if (*ar.what == 'C' || *ar.what == 't') {\n                    lua_pushliteral(L, \" ?\");  /* C function or tail call */\n\n                } else {\n                    lua_pushfstring(L, \" in function <%s:%d>\",\n                                    ar.short_src, ar.linedefined);\n                }\n            }\n        }\n\n        if (lua_gettop(L) - base >= 15) {\n            lua_concat(L, lua_gettop(L) - base);\n        }\n\n        /* check if the coroutine has a parent coroutine*/\n        coctx = coctx->parent_co_ctx;\n        if (!coctx || coctx->co_status == NGX_HTTP_LUA_CO_DEAD) {\n            break;\n        }\n\n        co = coctx->co;\n    }\n\n    lua_concat(L, lua_gettop(L) - base);\n    return 1;\n}\n\n\nint\nngx_http_lua_traceback(lua_State *L)\n{\n    if (!lua_isstring(L, 1)) { /* 'message' not a string? */\n        return 1;  /* keep it intact */\n    }\n\n    lua_getglobal(L, \"debug\");\n    if (!lua_istable(L, -1)) {\n        lua_pop(L, 1);\n        return 1;\n    }\n\n    lua_getfield(L, -1, \"traceback\");\n    if (!lua_isfunction(L, -1)) {\n        lua_pop(L, 2);\n        return 1;\n    }\n\n    lua_pushvalue(L, 1);  /* pass error message */\n    lua_pushinteger(L, 2);  /* skip this function and traceback */\n    lua_call(L, 2, 1);  /* call debug.traceback */\n    return 1;\n}\n\n\nstatic void\nngx_http_lua_inject_arg_api(lua_State *L)\n{\n    lua_pushliteral(L, \"arg\");\n    lua_newtable(L);    /*  .arg table aka {} */\n\n    lua_createtable(L, 0 /* narr */, 2 /* nrec */);    /*  the metatable */\n\n    lua_pushcfunction(L, ngx_http_lua_param_set);\n    lua_setfield(L, -2, \"__newindex\");\n\n    lua_setmetatable(L, -2);    /*  tie the metatable to param table */\n\n    dd(\"top: %d, type -1: %s\", lua_gettop(L), luaL_typename(L, -1));\n\n    lua_rawset(L, -3);    /*  set ngx.arg table */\n}\n\n\nstatic int\nngx_http_lua_param_set(lua_State *L)\n{\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_http_request_t          *r;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return 0;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"ctx not found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_BODY_FILTER);\n\n    return ngx_http_lua_body_filter_param_set(L, r, ctx);\n}\n\n\nngx_http_lua_co_ctx_t *\nngx_http_lua_get_co_ctx(lua_State *L, ngx_http_lua_ctx_t *ctx)\n{\n#ifdef HAVE_LUA_EXDATA2\n    return (ngx_http_lua_co_ctx_t *) lua_getexdata2(L);\n#else\n    ngx_uint_t                   i;\n    ngx_list_part_t             *part;\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    if (L == ctx->entry_co_ctx.co) {\n        return &ctx->entry_co_ctx;\n    }\n\n    if (ctx->user_co_ctx == NULL) {\n        return NULL;\n    }\n\n    part = &ctx->user_co_ctx->part;\n    coctx = part->elts;\n\n    /* FIXME: we should use rbtree here to prevent O(n) lookup overhead */\n\n    for (i = 0; /* void */; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n\n            part = part->next;\n            coctx = part->elts;\n            i = 0;\n        }\n\n        if (coctx[i].co == L) {\n            return &coctx[i];\n        }\n    }\n\n    return NULL;\n#endif\n}\n\n\nngx_http_lua_co_ctx_t *\nngx_http_lua_create_co_ctx(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx)\n{\n    ngx_http_lua_co_ctx_t       *coctx;\n\n    if (ctx->user_co_ctx == NULL) {\n        ctx->user_co_ctx = ngx_list_create(r->pool, 4,\n                                           sizeof(ngx_http_lua_co_ctx_t));\n        if (ctx->user_co_ctx == NULL) {\n            return NULL;\n        }\n    }\n\n    coctx = ngx_list_push(ctx->user_co_ctx);\n    if (coctx == NULL) {\n        return NULL;\n    }\n\n    ngx_memzero(coctx, sizeof(ngx_http_lua_co_ctx_t));\n\n    coctx->next_zombie_child_thread = &coctx->zombie_child_threads;\n    coctx->co_ref = LUA_NOREF;\n\n    return coctx;\n}\n\n\n/* this is for callers other than the content handler */\nngx_int_t\nngx_http_lua_run_posted_threads(ngx_connection_t *c, lua_State *L,\n    ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_uint_t nreqs)\n{\n    ngx_int_t                        rc;\n    ngx_http_lua_posted_thread_t    *pt;\n\n    for ( ;; ) {\n        if (c->destroyed || c->requests != nreqs) {\n            return NGX_DONE;\n        }\n\n        pt = ctx->posted_threads;\n        if (pt == NULL) {\n            return NGX_DONE;\n        }\n\n        ctx->posted_threads = pt->next;\n\n        ngx_http_lua_probe_run_posted_thread(r, pt->co_ctx->co,\n                                             (int) pt->co_ctx->co_status);\n\n        if (pt->co_ctx->co_status != NGX_HTTP_LUA_CO_RUNNING) {\n            continue;\n        }\n\n        ctx->cur_co_ctx = pt->co_ctx;\n\n        rc = ngx_http_lua_run_thread(L, r, ctx, 0);\n\n        if (rc == NGX_AGAIN) {\n            continue;\n        }\n\n        if (rc == NGX_DONE) {\n            ngx_http_lua_finalize_request(r, NGX_DONE);\n            continue;\n        }\n\n        /* rc == NGX_ERROR || rc >= NGX_OK */\n\n        if (ctx->entered_content_phase) {\n            ngx_http_lua_finalize_request(r, rc);\n        }\n\n        return rc;\n    }\n\n    /* impossible to reach here */\n}\n\n\nngx_int_t\nngx_http_lua_post_thread(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx,\n    ngx_http_lua_co_ctx_t *coctx)\n{\n    ngx_http_lua_posted_thread_t  **p;\n    ngx_http_lua_posted_thread_t   *pt;\n\n    pt = ngx_palloc(r->pool, sizeof(ngx_http_lua_posted_thread_t));\n    if (pt == NULL) {\n        return NGX_ERROR;\n    }\n\n    pt->co_ctx = coctx;\n    pt->next = NULL;\n\n    for (p = &ctx->posted_threads; *p; p = &(*p)->next) { /* void */ }\n\n    *p = pt;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_lua_finalize_threads(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, lua_State *L)\n{\n    int                              ref;\n    ngx_uint_t                       i;\n    ngx_list_part_t                 *part;\n    ngx_http_lua_co_ctx_t           *cc, *coctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http lua finalize threads\");\n\n#if 1\n    coctx = ctx->on_abort_co_ctx;\n    if (coctx && coctx->co_ref != LUA_NOREF) {\n        if (coctx->co_status != NGX_HTTP_LUA_CO_SUSPENDED) {\n            /* the on_abort thread contributes to the coctx->uthreads\n             * counter only when it actually starts running */\n            ngx_http_lua_cleanup_pending_operation(coctx);\n            ctx->uthreads--;\n        }\n\n        ngx_http_lua_del_thread(r, L, ctx, coctx);\n        ctx->on_abort_co_ctx = NULL;\n    }\n#endif\n\n    if (ctx->user_co_ctx) {\n        part = &ctx->user_co_ctx->part;\n        cc = part->elts;\n\n        for (i = 0; /* void */; i++) {\n\n            if (i >= part->nelts) {\n                if (part->next == NULL) {\n                    break;\n                }\n\n                part = part->next;\n                cc = part->elts;\n                i = 0;\n            }\n\n            coctx = &cc[i];\n\n            ref = coctx->co_ref;\n\n            if (ref != LUA_NOREF) {\n                ngx_http_lua_cleanup_pending_operation(coctx);\n\n                ngx_http_lua_del_thread(r, L, ctx, coctx);\n\n                ctx->uthreads--;\n            }\n        }\n\n        ctx->user_co_ctx = NULL;\n    }\n\n    ngx_http_lua_assert(ctx->uthreads == 0);\n\n    coctx = &ctx->entry_co_ctx;\n\n    ref = coctx->co_ref;\n    if (ref != LUA_NOREF) {\n        ngx_http_lua_cleanup_pending_operation(coctx);\n        ngx_http_lua_del_thread(r, L, ctx, coctx);\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_lua_post_zombie_thread(ngx_http_request_t *r,\n    ngx_http_lua_co_ctx_t *parent, ngx_http_lua_co_ctx_t *thread)\n{\n    ngx_http_lua_posted_thread_t   *pt;\n\n    pt = ngx_palloc(r->pool, sizeof(ngx_http_lua_posted_thread_t));\n    if (pt == NULL) {\n        return NGX_ERROR;\n    }\n\n    pt->co_ctx = thread;\n    pt->next = NULL;\n\n    ngx_http_lua_assert(parent->next_zombie_child_thread != NULL);\n\n    *parent->next_zombie_child_thread = pt;\n    parent->next_zombie_child_thread = &pt->next;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_lua_cleanup_zombie_child_uthreads(ngx_http_request_t *r,\n    lua_State *L, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx)\n{\n    ngx_http_lua_posted_thread_t   *pt;\n\n    for (pt = coctx->zombie_child_threads; pt; pt = pt->next) {\n        if (pt->co_ctx->co_ref != LUA_NOREF) {\n            ngx_http_lua_del_thread(r, L, ctx, pt->co_ctx);\n            ctx->uthreads--;\n        }\n    }\n\n    coctx->zombie_child_threads = NULL;\n    coctx->next_zombie_child_thread = &coctx->zombie_child_threads;\n}\n\n\nngx_int_t\nngx_http_lua_check_broken_connection(ngx_http_request_t *r, ngx_event_t *ev)\n{\n    int                  n;\n    char                 buf[1];\n    ngx_err_t            err;\n    ngx_int_t            event;\n    ngx_connection_t    *c;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"http lua check client, write event:%d, \\\"%V\\\"\",\n                   ev->write, &r->uri);\n\n    c = r->connection;\n\n    if (c->error) {\n        if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {\n\n            event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;\n\n            if (ngx_del_event(ev, event, 0) != NGX_OK) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n        }\n\n        return NGX_HTTP_CLIENT_CLOSED_REQUEST;\n    }\n\n#if (NGX_HTTP_V2)\n    if (r->stream) {\n        return NGX_OK;\n    }\n#endif\n\n#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n\n        if (!ev->pending_eof) {\n            return NGX_OK;\n        }\n\n        ev->eof = 1;\n\n        if (ev->kq_errno) {\n            ev->error = 1;\n        }\n\n        ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,\n                      \"kevent() reported that client prematurely closed \"\n                      \"connection\");\n\n        return NGX_HTTP_CLIENT_CLOSED_REQUEST;\n    }\n\n#endif\n\n    n = recv(c->fd, buf, 1, MSG_PEEK);\n\n    err = ngx_socket_errno;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, err,\n                   \"http lua recv(): %d\", n);\n\n    if (ev->write && (n >= 0 || err == NGX_EAGAIN)) {\n        return NGX_OK;\n    }\n\n    if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {\n        dd(\"event is active\");\n\n        event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;\n\n#if 1\n        if (ngx_del_event(ev, event, 0) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n#endif\n    }\n\n    dd(\"HERE %d\", (int) n);\n\n    if (n > 0) {\n        return NGX_OK;\n    }\n\n    if (n == -1) {\n        if (err == NGX_EAGAIN) {\n            dd(\"HERE\");\n            return NGX_OK;\n        }\n\n        ev->error = 1;\n\n    } else { /* n == 0 */\n        err = 0;\n    }\n\n    ev->eof = 1;\n\n    ngx_log_error(NGX_LOG_INFO, ev->log, err,\n                  \"client prematurely closed connection\");\n\n    return NGX_HTTP_CLIENT_CLOSED_REQUEST;\n}\n\n\nvoid\nngx_http_lua_rd_check_broken_connection(ngx_http_request_t *r)\n{\n    ngx_int_t                   rc;\n    ngx_event_t                *rev;\n    ngx_http_lua_ctx_t         *ctx;\n\n    if (r->done) {\n        return;\n    }\n\n    rc = ngx_http_lua_check_broken_connection(r, r->connection->read);\n\n    if (rc == NGX_OK) {\n        return;\n    }\n\n    /* rc == NGX_ERROR || rc > NGX_OK */\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return;\n    }\n\n    if (ctx->on_abort_co_ctx == NULL) {\n        r->connection->error = 1;\n        ngx_http_lua_request_cleanup(ctx, 0);\n        ngx_http_lua_finalize_request(r, rc);\n        return;\n    }\n\n    if (ctx->on_abort_co_ctx->co_status != NGX_HTTP_LUA_CO_SUSPENDED) {\n\n        /* on_abort already run for the current request handler */\n\n        rev = r->connection->read;\n\n        if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) {\n            if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {\n                ngx_http_lua_request_cleanup(ctx, 0);\n                ngx_http_lua_finalize_request(r,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n        }\n\n        return;\n    }\n\n    ctx->uthreads++;\n    ctx->resume_handler = ngx_http_lua_on_abort_resume;\n    ctx->on_abort_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING;\n    ctx->cur_co_ctx = ctx->on_abort_co_ctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua waking up the on_abort callback thread\");\n\n    if (ctx->entered_content_phase) {\n        r->write_event_handler = ngx_http_lua_content_wev_handler;\n\n    } else {\n        r->write_event_handler = ngx_http_core_run_phases;\n    }\n\n    r->write_event_handler(r);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_on_abort_resume(ngx_http_request_t *r)\n{\n    lua_State                   *vm;\n    ngx_int_t                    rc;\n    ngx_uint_t                   nreqs;\n    ngx_connection_t            *c;\n    ngx_http_lua_ctx_t          *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->resume_handler = ngx_http_lua_wev_handler;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua resuming the on_abort callback thread\");\n\n#if 0\n    ngx_http_lua_probe_info(\"tcp resume\");\n#endif\n\n    c = r->connection;\n    vm = ngx_http_lua_get_lua_vm(r, ctx);\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(vm, r, ctx, 0);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run thread returned %d\", rc);\n\n    if (rc == NGX_AGAIN) {\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (ctx->entered_content_phase) {\n        ngx_http_lua_finalize_request(r, rc);\n        return NGX_DONE;\n    }\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_lua_test_expect(ngx_http_request_t *r)\n{\n    ngx_int_t   n;\n    ngx_str_t  *expect;\n\n    if (r->expect_tested\n        || r->headers_in.expect == NULL\n        || r->http_version < NGX_HTTP_VERSION_11)\n    {\n        return NGX_OK;\n    }\n\n    r->expect_tested = 1;\n\n    expect = &r->headers_in.expect->value;\n\n    if (expect->len != sizeof(\"100-continue\") - 1\n        || ngx_strncasecmp(expect->data, (u_char *) \"100-continue\",\n                           sizeof(\"100-continue\") - 1)\n           != 0)\n    {\n        return NGX_OK;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"send 100 Continue\");\n\n    n = r->connection->send(r->connection,\n                            (u_char *) \"HTTP/1.1 100 Continue\" CRLF CRLF,\n                            sizeof(\"HTTP/1.1 100 Continue\" CRLF CRLF) - 1);\n\n    if (n == sizeof(\"HTTP/1.1 100 Continue\" CRLF CRLF) - 1) {\n        return NGX_OK;\n    }\n\n    /* we assume that such small packet should be send successfully */\n\n    return NGX_ERROR;\n}\n\n\nvoid\nngx_http_lua_finalize_request(ngx_http_request_t *r, ngx_int_t rc)\n{\n    ngx_http_lua_ctx_t              *ctx;\n#if (NGX_HTTP_SSL)\n#if HAVE_LUA_PROXY_SSL\n    ngx_http_upstream_t             *u;\n    ngx_connection_t                *c;\n    ngx_http_lua_ssl_ctx_t          *cctx;\n#endif\n#endif\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx && ctx->cur_co_ctx) {\n        ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);\n    }\n\n#if (NGX_HTTP_SSL)\n#if HAVE_LUA_PROXY_SSL\n    u = r->upstream;\n    if (u) {\n        c = u->peer.connection;\n        if (c && c->ssl) {\n            cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n            if (cctx && cctx->pool) {\n                if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n                    cctx->exit_code = 0;\n                }\n\n                if (r->main->count > cctx->original_request_count) {\n                    r->main->count--;\n                    return;\n                }\n\n                ngx_destroy_pool(cctx->pool);\n                cctx->pool = NULL;\n\n                return;\n            }\n        }\n    }\n#endif\n#endif\n\n    if (r->connection->fd != (ngx_socket_t) -1) {\n        ngx_http_finalize_request(r, rc);\n        return;\n    }\n\n    ngx_http_lua_finalize_fake_request(r, rc);\n}\n\n\nvoid\nngx_http_lua_finalize_fake_request(ngx_http_request_t *r, ngx_int_t rc)\n{\n    ngx_connection_t          *c;\n#if (NGX_HTTP_SSL)\n    ngx_ssl_conn_t            *ssl_conn;\n    ngx_http_lua_ssl_ctx_t    *cctx;\n#endif\n\n    c = r->connection;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http lua finalize fake request: %d, a:%d, c:%d\",\n                   rc, r == c->data, r->main->count);\n\n    if (rc == NGX_DONE) {\n        ngx_http_lua_close_fake_request(r);\n        return;\n    }\n\n    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n\n#if (NGX_HTTP_SSL)\n\n        if (r->connection->ssl) {\n            ssl_conn = r->connection->ssl->connection;\n            if (ssl_conn) {\n                c = ngx_ssl_get_connection(ssl_conn);\n\n                if (c && c->ssl) {\n                    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);\n                    if (cctx != NULL) {\n                        cctx->exit_code = 0;\n                    }\n                }\n            }\n        }\n\n#endif\n\n        ngx_http_lua_close_fake_request(r);\n        return;\n    }\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    if (c->write->timer_set) {\n        c->write->delayed = 0;\n        ngx_del_timer(c->write);\n    }\n\n    ngx_http_lua_close_fake_request(r);\n}\n\n\nstatic void\nngx_http_lua_close_fake_request(ngx_http_request_t *r)\n{\n    ngx_connection_t  *c;\n\n    r = r->main;\n    c = r->connection;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http lua fake request count:%d\", r->count);\n\n    if (r->count == 0) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0, \"http lua fake request \"\n                      \"count is zero\");\n    }\n\n    r->count--;\n\n    if (r->count) {\n        return;\n    }\n\n    ngx_http_lua_free_fake_request(r);\n    ngx_http_lua_close_fake_connection(c);\n}\n\n\nvoid\nngx_http_lua_free_fake_request(ngx_http_request_t *r)\n{\n    ngx_log_t                 *log;\n    ngx_http_cleanup_t        *cln;\n\n    log = r->connection->log;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, \"http lua close fake \"\n                   \"request\");\n\n    if (r->pool == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0, \"http lua fake request \"\n                      \"already closed\");\n        return;\n    }\n\n    cln = r->cleanup;\n    r->cleanup = NULL;\n\n    while (cln) {\n        if (cln->handler) {\n            cln->handler(cln->data);\n        }\n\n        cln = cln->next;\n    }\n\n    r->request_line.len = 0;\n\n    r->connection->destroyed = 1;\n}\n\n\nvoid\nngx_http_lua_close_fake_connection(ngx_connection_t *c)\n{\n    ngx_pool_t          *pool;\n    ngx_connection_t    *saved_c = NULL;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http lua close fake http connection %p\", c);\n\n    c->destroyed = 1;\n\n    pool = c->pool;\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    if (c->write->timer_set) {\n        ngx_del_timer(c->write);\n    }\n\n    c->read->closed = 1;\n    c->write->closed = 1;\n\n    /* When destroying the pool, the registered clean callbacks will be\n     * executed. If the ngx_connection_t is freed before these callbacks are\n     * run, and a new ngx_connection_t is created within a clean callback,\n     * it is possible for the freed ngx_connection_t to be reused again.\n     * If this reused ngx_connection_t is destroyed again within the clean\n     * callback logic, it may result in other clean callbacks holding a\n     * ngx_connection_t that has already been destroyed.\n     */\n    if (pool) {\n        ngx_destroy_pool(pool);\n    }\n\n    /* we temporarily use a valid fd (0) to make ngx_free_connection happy */\n\n    c->fd = 0;\n\n    if (ngx_cycle->files) {\n        saved_c = ngx_cycle->files[0];\n    }\n\n    ngx_free_connection(c);\n\n    c->fd = (ngx_socket_t) -1;\n\n    if (ngx_cycle->files) {\n        ngx_cycle->files[0] = saved_c;\n    }\n}\n\n\nngx_int_t\nngx_http_lua_init_vm(lua_State **new_vm, lua_State *parent_vm,\n    ngx_cycle_t *cycle, ngx_pool_t *pool, ngx_http_lua_main_conf_t *lmcf,\n    ngx_log_t *log, ngx_pool_cleanup_t **pcln)\n{\n    int                              rc;\n    lua_State                       *L;\n    ngx_uint_t                       i;\n    ngx_pool_cleanup_t              *cln;\n    ngx_http_lua_preload_hook_t     *hook;\n    ngx_http_lua_vm_state_t         *state;\n\n    cln = ngx_pool_cleanup_add(pool, 0);\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    /* create new Lua VM instance */\n    L = ngx_http_lua_new_state(parent_vm, cycle, lmcf, log);\n    if (L == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, \"lua initialize the \"\n                   \"global Lua VM %p\", L);\n\n    /* register cleanup handler for Lua VM */\n    cln->handler = ngx_http_lua_cleanup_vm;\n\n    state = ngx_alloc(sizeof(ngx_http_lua_vm_state_t), log);\n    if (state == NULL) {\n        return NGX_ERROR;\n    }\n\n    state->vm = L;\n    state->count = 1;\n\n    cln->data = state;\n\n    if (lmcf->vm_cleanup == NULL) {\n        /* this assignment will happen only once,\n         * and also only for the main Lua VM */\n        lmcf->vm_cleanup = cln;\n    }\n\n    if (pcln) {\n        *pcln = cln;\n    }\n\n#ifdef OPENRESTY_LUAJIT\n    /* load FFI library first since cdata needs it */\n    luaopen_ffi(L);\n#endif\n\n    if (lmcf->preload_hooks) {\n\n        /* register the 3rd-party module's preload hooks */\n\n        lua_getglobal(L, \"package\");\n        lua_getfield(L, -1, \"preload\");\n\n        hook = lmcf->preload_hooks->elts;\n\n        for (i = 0; i < lmcf->preload_hooks->nelts; i++) {\n\n            ngx_http_lua_probe_register_preload_package(L,\n                                                        hook[i].package);\n\n            lua_pushcfunction(L, hook[i].loader);\n            lua_setfield(L, -2, (char *) hook[i].package);\n        }\n\n        lua_pop(L, 2);\n    }\n\n    *new_vm = L;\n\n    lua_getglobal(L, \"require\");\n    lua_pushstring(L, \"resty.core\");\n\n    rc = lua_pcall(L, 1, 1, 0);\n    if (rc != 0) {\n        return NGX_DECLINED;\n    }\n\n#ifdef OPENRESTY_LUAJIT\n    ngx_http_lua_inject_global_write_guard(L, log);\n#endif\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_lua_cleanup_vm(void *data)\n{\n    lua_State                       *L;\n    ngx_http_lua_vm_state_t         *state = data;\n\n#if (DDEBUG)\n    if (state) {\n        dd(\"cleanup VM: c:%d, s:%p\", (int) state->count, state->vm);\n    }\n#endif\n\n    if (state) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua decrementing the reference count for Lua VM: %i\",\n                       state->count);\n\n        if (--state->count == 0) {\n            L = state->vm;\n            ngx_http_lua_cleanup_conn_pools(L);\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                           \"lua close the global Lua VM %p\", L);\n            lua_close(L);\n            ngx_free(state);\n        }\n    }\n}\n\n\nngx_connection_t *\nngx_http_lua_create_fake_connection(ngx_pool_t *pool)\n{\n    ngx_log_t               *log;\n    ngx_connection_t        *c;\n    ngx_connection_t        *saved_c = NULL;\n\n    /* (we temporarily use a valid fd (0) to make ngx_get_connection happy) */\n    if (ngx_cycle->files) {\n        saved_c = ngx_cycle->files[0];\n    }\n\n    c = ngx_get_connection(0, ngx_cycle->log);\n\n    if (ngx_cycle->files) {\n        ngx_cycle->files[0] = saved_c;\n    }\n\n    if (c == NULL) {\n        return NULL;\n    }\n\n    c->fd = (ngx_socket_t) -1;\n    c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);\n\n    if (pool) {\n        c->pool = pool;\n\n    } else {\n        c->pool = ngx_create_pool(128, c->log);\n        if (c->pool == NULL) {\n            goto failed;\n        }\n    }\n\n    log = ngx_pcalloc(c->pool, sizeof(ngx_log_t));\n    if (log == NULL) {\n        goto failed;\n    }\n\n    c->log = log;\n    c->log->connection = c->number;\n    c->log->action = NULL;\n    c->log->data = NULL;\n\n    c->log_error = NGX_ERROR_INFO;\n\n#if 0\n    c->buffer = ngx_create_temp_buf(c->pool, 2);\n    if (c->buffer == NULL) {\n        goto failed;\n    }\n\n    c->buffer->start[0] = CR;\n    c->buffer->start[1] = LF;\n#endif\n\n    c->error = 1;\n\n    dd(\"created fake connection: %p\", c);\n\n    return c;\n\nfailed:\n\n    ngx_http_lua_close_fake_connection(c);\n    return NULL;\n}\n\n\nngx_http_request_t *\nngx_http_lua_create_fake_request(ngx_connection_t *c)\n{\n    ngx_http_request_t      *r;\n\n    r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t));\n    if (r == NULL) {\n        return NULL;\n    }\n\n    c->requests++;\n\n    r->pool = c->pool;\n\n    dd(\"r pool allocated: %d\", (int) (sizeof(ngx_http_lua_ctx_t)\n       + sizeof(void *) * ngx_http_max_module + sizeof(ngx_http_cleanup_t)));\n\n#if 0\n    hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));\n    if (hc == NULL) {\n        goto failed;\n    }\n\n    r->header_in = c->buffer;\n    r->header_end = c->buffer->start;\n\n    if (ngx_list_init(&r->headers_out.headers, r->pool, 0,\n                      sizeof(ngx_table_elt_t))\n        != NGX_OK)\n    {\n        goto failed;\n    }\n\n    if (ngx_list_init(&r->headers_in.headers, r->pool, 0,\n                      sizeof(ngx_table_elt_t))\n        != NGX_OK)\n    {\n        goto failed;\n    }\n#endif\n\n    r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);\n    if (r->ctx == NULL) {\n        return NULL;\n    }\n\n#if 0\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts\n                               * sizeof(ngx_http_variable_value_t));\n    if (r->variables == NULL) {\n        goto failed;\n    }\n#endif\n\n    r->connection = c;\n\n    r->headers_in.content_length_n = 0;\n    c->data = r;\n#if 0\n    hc->request = r;\n    r->http_connection = hc;\n#endif\n    r->signature = NGX_HTTP_MODULE;\n    r->main = r;\n    r->count = 1;\n\n    r->method = NGX_HTTP_UNKNOWN;\n\n    r->headers_in.keep_alive_n = -1;\n    r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;\n    r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1;\n\n    r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;\n    r->discard_body = 1;\n\n    dd(\"created fake request %p\", r);\n\n    return r;\n}\n\n\nngx_int_t\nngx_http_lua_report(ngx_log_t *log, lua_State *L, int status,\n    const char *prefix)\n{\n    const char      *msg;\n\n    if (status && !lua_isnil(L, -1)) {\n        msg = lua_tostring(L, -1);\n        if (msg == NULL) {\n            msg = \"unknown error\";\n        }\n\n        ngx_log_error(NGX_LOG_ERR, log, 0, \"%s error: %s\", prefix, msg);\n        lua_pop(L, 1);\n    }\n\n    /* force a full garbage-collection cycle */\n    lua_gc(L, LUA_GCCOLLECT, 0);\n\n    return status == 0 ? NGX_OK : NGX_ERROR;\n}\n\n\nint\nngx_http_lua_do_call(ngx_log_t *log, lua_State *L)\n{\n    int                 status, base;\n#if (NGX_PCRE)\n    ngx_pool_t         *old_pool;\n#endif\n\n    base = lua_gettop(L);  /* function index */\n    lua_pushcfunction(L, ngx_http_lua_traceback);  /* push traceback function */\n    lua_insert(L, base);  /* put it under chunk and args */\n\n#if (NGX_PCRE)\n    old_pool = ngx_http_lua_pcre_malloc_init(ngx_cycle->pool);\n#endif\n\n    status = lua_pcall(L, 0, 0, base);\n\n#if (NGX_PCRE)\n    ngx_http_lua_pcre_malloc_done(old_pool);\n#endif\n\n    lua_remove(L, base);\n\n    return status;\n}\n\n\nstatic int\nngx_http_lua_get_raw_phase_context(lua_State *L)\n{\n    ngx_http_request_t      *r;\n    ngx_http_lua_ctx_t      *ctx;\n\n#ifdef OPENRESTY_LUAJIT\n    r = lua_getexdata(L);\n#else\n    r = lua_touserdata(L, 1);\n#endif\n\n    if (r == NULL) {\n        return 0;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return 0;\n    }\n\n    lua_pushinteger(L, (int) ctx->context);\n    return 1;\n}\n\n\nngx_http_cleanup_t *\nngx_http_lua_cleanup_add(ngx_http_request_t *r, size_t size)\n{\n    ngx_http_cleanup_t  *cln;\n    ngx_http_lua_ctx_t  *ctx;\n\n    if (size == 0) {\n        ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n\n        r = r->main;\n\n        if (ctx != NULL && ctx->free_cleanup) {\n            cln = ctx->free_cleanup;\n            ctx->free_cleanup = cln->next;\n\n            dd(\"reuse cleanup: %p\", cln);\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"lua http cleanup reuse: %p\", cln);\n\n            cln->handler = NULL;\n            cln->next = r->cleanup;\n\n            r->cleanup = cln;\n\n            return cln;\n        }\n    }\n\n    return ngx_http_cleanup_add(r, size);\n}\n\n\nvoid\nngx_http_lua_cleanup_free(ngx_http_request_t *r, ngx_http_cleanup_pt *cleanup)\n{\n    ngx_http_cleanup_t  **last;\n    ngx_http_cleanup_t   *cln;\n    ngx_http_lua_ctx_t   *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return;\n    }\n\n    r = r->main;\n\n    cln = (ngx_http_cleanup_t *)\n              ((u_char *) cleanup - offsetof(ngx_http_cleanup_t, handler));\n\n    dd(\"cln: %p, cln->handler: %p, &cln->handler: %p\",\n       cln, cln->handler, &cln->handler);\n\n    last = &r->cleanup;\n\n    while (*last) {\n        if (*last == cln) {\n            *last = cln->next;\n\n            cln->next = ctx->free_cleanup;\n            ctx->free_cleanup = cln;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"lua http cleanup free: %p\", cln);\n\n            return;\n        }\n\n        last = &(*last)->next;\n    }\n}\n\n\n#if (NGX_HTTP_LUA_HAVE_SA_RESTART)\nvoid\nngx_http_lua_set_sa_restart(ngx_log_t *log)\n{\n    int                    *signo;\n    int                     sigs[] = NGX_HTTP_LUA_SA_RESTART_SIGS;\n    struct sigaction        act;\n\n    for (signo = sigs; *signo != 0; signo++) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,\n                       \"setting SA_RESTART for signal %d\", *signo);\n\n        if (sigaction(*signo, NULL, &act) != 0) {\n            ngx_log_error(NGX_LOG_WARN, log, ngx_errno, \"failed to get \"\n                          \"sigaction for signal %d\", *signo);\n        }\n\n        act.sa_flags |= SA_RESTART;\n\n        if (sigaction(*signo, &act, NULL) != 0) {\n            ngx_log_error(NGX_LOG_WARN, log, ngx_errno, \"failed to set \"\n                          \"sigaction for signal %d\", *signo);\n        }\n    }\n}\n#endif\n\n\nsize_t\nngx_http_lua_escape_log(u_char *dst, u_char *src, size_t size)\n{\n    size_t          n;\n    u_char          c;\n    static u_char   hex[] = \"0123456789ABCDEF\";\n\n    static uint32_t escape[] = {\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0x00000004, /* 0000 0000 0000 0000  0000 0000 0000 0100 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x10000000, /* 0001 0000 0000 0000  0000 0000 0000 0000 */\n\n                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */\n\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n    };\n\n    if (dst == NULL) {\n\n        /* find the number of characters to be escaped */\n\n        n = 0;\n\n        while (size) {\n            c = *src;\n            if (escape[c >> 5] & (1 << (c & 0x1f))) {\n                n += 4;\n\n            } else {\n                n++;\n            }\n\n            src++;\n            size--;\n        }\n\n        return n;\n    }\n\n    while (size) {\n        c = *src;\n        if (escape[c >> 5] & (1 << (c & 0x1f))) {\n            *dst++ = '\\\\';\n            *dst++ = 'x';\n            *dst++ = hex[*src >> 4];\n            *dst++ = hex[*src & 0xf];\n            src++;\n\n        } else {\n            *dst++ = *src++;\n        }\n\n        size--;\n    }\n\n    return 0;\n}\n\n\nngx_int_t\nngx_http_lua_copy_escaped_header(ngx_http_request_t *r,\n    ngx_str_t *dst, int is_name)\n{\n    size_t       escape;\n    size_t       len;\n    u_char      *data;\n    int          type;\n\n    type = is_name\n        ? NGX_HTTP_LUA_ESCAPE_HEADER_NAME : NGX_HTTP_LUA_ESCAPE_HEADER_VALUE;\n\n    data = dst->data;\n    len = dst->len;\n\n    escape = ngx_http_lua_escape_uri(NULL, data, len, type);\n    if (escape > 0) {\n        /*\n         * we allocate space for the trailing '\\0' char here because nginx\n         * header values must be null-terminated\n         */\n        dst->data = ngx_palloc(r->pool, len + 2 * escape + 1);\n        if (dst->data == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_lua_escape_uri(dst->data, data, len, type);\n        dst->len = len + 2 * escape;\n        dst->data[dst->len] = '\\0';\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_lua_decode_base64mime(ngx_str_t *dst, ngx_str_t *src)\n{\n    size_t          i;\n    u_char         *d, *s, ch;\n    size_t          data_len = 0;\n    u_char          buf[4];\n    size_t          buf_len = 0;\n    static u_char   basis[] = {\n        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,\n        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,\n        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63,\n        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77,\n        77,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,\n        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77,\n        77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\n        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77,\n\n        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,\n        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,\n        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,\n        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,\n        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,\n        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,\n        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,\n        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77\n    };\n\n    for (i = 0; i < src->len; i++) {\n        ch = src->data[i];\n        if (ch == '=') {\n            break;\n        }\n\n        if (basis[ch] == 77) {\n            continue;\n        }\n\n        data_len++;\n    }\n\n    if (data_len % 4 == 1) {\n        return NGX_ERROR;\n    }\n\n    s = src->data;\n    d = dst->data;\n\n    for (i = 0; i < src->len; i++) {\n        if (s[i] == '=') {\n            break;\n        }\n\n        if (basis[s[i]] == 77) {\n            continue;\n        }\n\n        buf[buf_len++] = s[i];\n        if (buf_len == 4) {\n            *d++ = (u_char) (basis[buf[0]] << 2 | basis[buf[1]] >> 4);\n            *d++ = (u_char) (basis[buf[1]] << 4 | basis[buf[2]] >> 2);\n            *d++ = (u_char) (basis[buf[2]] << 6 | basis[buf[3]]);\n            buf_len = 0;\n        }\n    }\n\n    if (buf_len > 1) {\n        *d++ = (u_char) (basis[buf[0]] << 2 | basis[buf[1]] >> 4);\n    }\n\n    if (buf_len > 2) {\n        *d++ = (u_char) (basis[buf[1]] << 4 | basis[buf[2]] >> 2);\n    }\n\n    dst->len = d - dst->data;\n\n    return NGX_OK;\n}\n\n\nngx_addr_t *\nngx_http_lua_parse_addr(lua_State *L, u_char *text, size_t len)\n{\n    ngx_addr_t           *addr;\n    size_t                socklen;\n    in_addr_t             inaddr;\n    ngx_uint_t            family;\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    struct in6_addr       inaddr6;\n    struct sockaddr_in6  *sin6;\n\n    /*\n     * prevent MSVC8 warning:\n     *    potentially uninitialized local variable 'inaddr6' used\n     */\n    ngx_memzero(&inaddr6, sizeof(struct in6_addr));\n#endif\n\n    inaddr = ngx_inet_addr(text, len);\n\n    if (inaddr != INADDR_NONE) {\n        family = AF_INET;\n        socklen = sizeof(struct sockaddr_in);\n\n#if (NGX_HAVE_INET6)\n\n    } else if (ngx_inet6_addr(text, len, inaddr6.s6_addr) == NGX_OK) {\n        family = AF_INET6;\n        socklen = sizeof(struct sockaddr_in6);\n#endif\n\n    } else {\n        return NULL;\n    }\n\n    addr = lua_newuserdata(L, sizeof(ngx_addr_t) + socklen + len);\n    if (addr == NULL) {\n        luaL_error(L, \"no memory\");\n        return NULL;\n    }\n\n    addr->sockaddr = (struct sockaddr *) ((u_char *) addr + sizeof(ngx_addr_t));\n\n    ngx_memzero(addr->sockaddr, socklen);\n\n    addr->sockaddr->sa_family = (u_char) family;\n    addr->socklen = socklen;\n\n    switch (family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) addr->sockaddr;\n        ngx_memcpy(sin6->sin6_addr.s6_addr, inaddr6.s6_addr, 16);\n        break;\n#endif\n\n    default: /* AF_INET */\n        sin = (struct sockaddr_in *) addr->sockaddr;\n        sin->sin_addr.s_addr = inaddr;\n        break;\n    }\n\n    addr->name.data = (u_char *) addr->sockaddr + socklen;\n    addr->name.len = len;\n    ngx_memcpy(addr->name.data, text, len);\n\n    return addr;\n}\n\n\nvoid\nngx_http_lua_ffi_bypass_if_checks(ngx_http_request_t *r)\n{\n    r->disable_not_modified = 1;\n}\n\n\n#if (HAVE_QUIC_SSL_LUA_YIELD_PATCH && NGX_HTTP_V3)\nvoid\nngx_http_lua_resume_quic_ssl_handshake(ngx_connection_t *c)\n{\n    ngx_int_t        rc, sslerr;\n    ngx_ssl_conn_t  *ssl_conn;\n\n    if (c == NULL || c->ssl == NULL || c->ssl->connection == NULL) {\n        return;\n    }\n\n    if (!c->udp || c->read == NULL\n        || c->read->handler != ngx_quic_input_handler)\n    {\n        return;\n    }\n\n    ssl_conn = c->ssl->connection;\n\n    /* Openresty ssl lua scripts are triggered by registering callbacks into\n     * openssl. If a lua script calls a yield api during execution, openssl's\n     * SSL_do_handshake will return a specific error code. The application\n     * should not treat these codes as fatal. The lua script will resume on\n     * yield-related events until it finishes. After completion,\n     * SSL_do_handshake should be called again to advance openssl's state\n     * machine.\n     *\n     * Note that nginx quic and openresty ssl lua scripts are independent. When\n     * openresty ssl lua runs, the client is waiting for a server response, so\n     * nginx quic does not affect the execution of the lua script. After the lua\n     * script finishes, SSL_do_handshake is called again, and nginx quic's\n     * registered callback continues the handshake.\n     */\n    rc = SSL_do_handshake(ssl_conn);\n    sslerr = SSL_get_error(ssl_conn, rc);\n\n    if (rc <= 0 && sslerr != SSL_ERROR_WANT_READ\n#if OPENSSL_VERSION_NUMBER >= 0x10002000L\n        && sslerr != SSL_ERROR_WANT_X509_LOOKUP\n#endif\n#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB\n        && sslerr != SSL_ERROR_WANT_CLIENT_HELLO_CB\n#endif\n    ) {\n        /* If a fatal error occurs or lua script exits with error during quic\n         * handshake, the quic connection will be closed immediately.\n         */\n        ngx_quic_close_connection(c, NGX_ERROR);\n\n    } else {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"lua resuming quic ssl handshake: rc %d, err %d, will \"\n                       \"continue driving handshake or next lua script phase\",\n                       rc, sslerr);\n    }\n}\n#endif\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_util.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_UTIL_H_INCLUDED_\n#define _NGX_HTTP_LUA_UTIL_H_INCLUDED_\n\n\n#ifdef DDEBUG\n#include \"ddebug.h\"\n#endif\n\n\n#include \"ngx_http_lua_common.h\"\n#include \"ngx_http_lua_ssl.h\"\n#include \"ngx_http_lua_api.h\"\n\n\n#ifndef NGX_UNESCAPE_URI_COMPONENT\n#   define NGX_UNESCAPE_URI_COMPONENT 0\n#endif\n\n\n#ifndef NGX_HTTP_SWITCHING_PROTOCOLS\n#   define NGX_HTTP_SWITCHING_PROTOCOLS 101\n#endif\n\n#define NGX_HTTP_LUA_ESCAPE_HEADER_NAME  7\n\n#define NGX_HTTP_LUA_ESCAPE_HEADER_VALUE  8\n\n#define NGX_HTTP_LUA_CONTEXT_YIELDABLE (NGX_HTTP_LUA_CONTEXT_REWRITE         \\\n                                | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE        \\\n                                | NGX_HTTP_LUA_CONTEXT_ACCESS                \\\n                                | NGX_HTTP_LUA_CONTEXT_PRECONTENT            \\\n                                | NGX_HTTP_LUA_CONTEXT_CONTENT               \\\n                                | NGX_HTTP_LUA_CONTEXT_TIMER                 \\\n                                | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT        \\\n                                | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY      \\\n                                | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO      \\\n                                | NGX_HTTP_LUA_CONTEXT_SSL_CERT              \\\n                                | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH)\n\n/* key in Lua vm registry for all the \"ngx.ctx\" tables */\n#define ngx_http_lua_ctx_tables_key  \"ngx_lua_ctx_tables\"\n\n#define ngx_http_lua_context_name(c)                                         \\\n    ((c) == NGX_HTTP_LUA_CONTEXT_SET ? \"set_by_lua*\"                         \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_REWRITE ? \"rewrite_by_lua*\"               \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE ? \"server_rewrite_by_lua*\" \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_ACCESS ? \"access_by_lua*\"                 \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_PRECONTENT ? \"precontent_by_lua*\"         \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_CONTENT ? \"content_by_lua*\"               \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_LOG ? \"log_by_lua*\"                       \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_HEADER_FILTER ? \"header_filter_by_lua*\"   \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_BODY_FILTER ? \"body_filter_by_lua*\"       \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_TIMER ? \"ngx.timer\"                       \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_INIT_WORKER ? \"init_worker_by_lua*\"       \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_EXIT_WORKER ? \"exit_worker_by_lua*\"       \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_BALANCER ? \"balancer_by_lua*\"             \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_CERT ?                          \\\n                                             \"proxy_ssl_certificate_by_lua*\" \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY ?                        \\\n                                                 \"proxy_ssl_verify_by_lua*\"  \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO ?                        \\\n                                                 \"ssl_client_hello_by_lua*\"  \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CERT ? \"ssl_certificate_by_lua*\"      \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE ?                          \\\n                                                 \"ssl_session_store_by_lua*\" \\\n     : (c) == NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH ?                          \\\n                                                 \"ssl_session_fetch_by_lua*\" \\\n     : \"(unknown)\")\n\n#define ngx_http_lua_check_context(L, ctx, flags)                            \\\n    if (!((ctx)->context & (flags))) {                                       \\\n        return luaL_error(L, \"API disabled in the context of %s\",            \\\n                          ngx_http_lua_context_name((ctx)->context));        \\\n    }\n\n\n#define ngx_http_lua_check_fake_request(L, r)                                \\\n    if ((r)->connection->fd == (ngx_socket_t) -1) {                          \\\n        return luaL_error(L, \"API disabled in the current context\");         \\\n    }\n\n\n#define ngx_http_lua_check_fake_request2(L, r, ctx)                          \\\n    if ((r)->connection->fd == (ngx_socket_t) -1) {                          \\\n        return luaL_error(L, \"API disabled in the context of %s\",            \\\n                          ngx_http_lua_context_name((ctx)->context));        \\\n    }\n\n\n#define ngx_http_lua_check_if_abortable(L, ctx)                              \\\n    if ((ctx)->no_abort) {                                                   \\\n        return luaL_error(L, \"attempt to abort with pending subrequests\");   \\\n    }\n\n\n#define ngx_http_lua_ssl_get_ctx(ssl_conn)                                   \\\n    SSL_get_ex_data(ssl_conn, ngx_http_lua_ssl_ctx_index)\n\n\n#define ngx_http_lua_hash_literal(s)                                         \\\n    ngx_http_lua_hash_str((u_char *) s, sizeof(s) - 1)\n\n\ntypedef struct {\n    ngx_http_lua_ffi_str_t   key;\n    ngx_http_lua_ffi_str_t   value;\n} ngx_http_lua_ffi_table_elt_t;\n\n\n/* char whose address we use as the key in Lua vm registry for\n * user code cache table */\nextern char ngx_http_lua_code_cache_key;\n\n/* char whose address we use as the key in Lua vm registry for\n * socket connection pool table */\nextern char ngx_http_lua_socket_pool_key;\n\n/* coroutine anchoring table key in Lua VM registry */\nextern char ngx_http_lua_coroutines_key;\n\n/* key to the metatable for ngx.req.get_headers() and ngx.resp.get_headers() */\nextern char ngx_http_lua_headers_metatable_key;\n\n\nstatic ngx_inline ngx_int_t\nngx_http_lua_ffi_check_context(ngx_http_lua_ctx_t *ctx, unsigned flags,\n    u_char *err, size_t *errlen)\n{\n    if (!(ctx->context & flags)) {\n        *errlen = ngx_snprintf(err, *errlen,\n                               \"API disabled in the context of %s\",\n                               ngx_http_lua_context_name((ctx)->context))\n                  - err;\n\n        return NGX_DECLINED;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t ngx_http_lua_init_vm(lua_State **new_vm, lua_State *parent_vm,\n    ngx_cycle_t *cycle, ngx_pool_t *pool, ngx_http_lua_main_conf_t *lmcf,\n    ngx_log_t *log, ngx_pool_cleanup_t **pcln);\n\nlua_State *ngx_http_lua_new_thread(ngx_http_request_t *r, lua_State *l,\n    int *ref);\n\nu_char *ngx_http_lua_rebase_path(ngx_pool_t *pool, u_char *src, size_t len);\n\nngx_int_t ngx_http_lua_send_header_if_needed(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx);\n\nngx_int_t ngx_http_lua_send_chain_link(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, ngx_chain_t *cl);\n\nvoid ngx_http_lua_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in);\n\nngx_int_t ngx_http_lua_add_copy_chain(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, ngx_chain_t ***plast, ngx_chain_t *in,\n    ngx_int_t *eof);\n\nvoid ngx_http_lua_reset_ctx(ngx_http_request_t *r, lua_State *L,\n    ngx_http_lua_ctx_t *ctx);\n\nvoid ngx_http_lua_generic_phase_post_read(ngx_http_request_t *r);\n\nvoid ngx_http_lua_request_cleanup(ngx_http_lua_ctx_t *ctx, int forcible);\n\nvoid ngx_http_lua_request_cleanup_handler(void *data);\n\nngx_int_t ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, volatile int nret);\n\nngx_int_t ngx_http_lua_wev_handler(ngx_http_request_t *r);\n\nu_char *ngx_http_lua_digest_hex(u_char *dest, const u_char *buf,\n    int buf_len);\n\nvoid ngx_http_lua_set_multi_value_table(lua_State *L, int index);\n\nvoid ngx_http_lua_unescape_uri(u_char **dst, u_char **src, size_t size,\n    ngx_uint_t type);\n\nuintptr_t ngx_http_lua_escape_uri(u_char *dst, u_char *src,\n    size_t size, ngx_uint_t type);\n\nngx_int_t ngx_http_lua_copy_escaped_header(ngx_http_request_t *r,\n    ngx_str_t *dst, int is_name);\n\nvoid ngx_http_lua_inject_req_api(ngx_log_t *log, lua_State *L);\n\nvoid ngx_http_lua_process_args_option(ngx_http_request_t *r,\n    lua_State *L, int table, ngx_str_t *args);\n\nngx_int_t ngx_http_lua_open_and_stat_file(u_char *name,\n    ngx_open_file_info_t *of, ngx_log_t *log);\n\nngx_chain_t *ngx_http_lua_chain_get_free_buf(ngx_log_t *log, ngx_pool_t *p,\n    ngx_chain_t **free, size_t len);\n\n#ifndef OPENRESTY_LUAJIT\nvoid ngx_http_lua_create_new_globals_table(lua_State *L, int narr, int nrec);\n#endif\n\nint ngx_http_lua_traceback(lua_State *L);\n\nngx_http_lua_co_ctx_t *ngx_http_lua_get_co_ctx(lua_State *L,\n    ngx_http_lua_ctx_t *ctx);\n\nngx_http_lua_co_ctx_t *ngx_http_lua_create_co_ctx(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx);\n\nngx_int_t ngx_http_lua_run_posted_threads(ngx_connection_t *c, lua_State *L,\n    ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_uint_t nreqs);\n\nngx_int_t ngx_http_lua_post_thread(ngx_http_request_t *r,\n    ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx);\n\nvoid ngx_http_lua_del_thread(ngx_http_request_t *r, lua_State *L,\n    ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx);\n\nvoid ngx_http_lua_rd_check_broken_connection(ngx_http_request_t *r);\n\nngx_int_t ngx_http_lua_test_expect(ngx_http_request_t *r);\n\nngx_int_t ngx_http_lua_check_broken_connection(ngx_http_request_t *r,\n    ngx_event_t *ev);\n\nvoid ngx_http_lua_finalize_request(ngx_http_request_t *r, ngx_int_t rc);\n\nvoid ngx_http_lua_finalize_fake_request(ngx_http_request_t *r,\n    ngx_int_t rc);\n\nvoid ngx_http_lua_close_fake_connection(ngx_connection_t *c);\n\nvoid ngx_http_lua_free_fake_request(ngx_http_request_t *r);\n\nvoid ngx_http_lua_release_ngx_ctx_table(ngx_log_t *log, lua_State *L,\n    ngx_http_lua_ctx_t *ctx);\n\nvoid ngx_http_lua_cleanup_vm(void *data);\n\nngx_connection_t *ngx_http_lua_create_fake_connection(ngx_pool_t *pool);\n\nngx_http_request_t *ngx_http_lua_create_fake_request(ngx_connection_t *c);\n\nngx_int_t ngx_http_lua_report(ngx_log_t *log, lua_State *L, int status,\n    const char *prefix);\n\nint ngx_http_lua_do_call(ngx_log_t *log, lua_State *L);\n\nngx_http_cleanup_t *ngx_http_lua_cleanup_add(ngx_http_request_t *r,\n    size_t size);\n\nvoid ngx_http_lua_cleanup_free(ngx_http_request_t *r,\n    ngx_http_cleanup_pt *cleanup);\n\n#if (NGX_HTTP_LUA_HAVE_SA_RESTART)\nvoid ngx_http_lua_set_sa_restart(ngx_log_t *log);\n#endif\nngx_int_t ngx_http_lua_decode_base64mime(ngx_str_t *dst, ngx_str_t *src);\n\nngx_addr_t *ngx_http_lua_parse_addr(lua_State *L, u_char *text, size_t len);\n\nsize_t ngx_http_lua_escape_log(u_char *dst, u_char *src, size_t size);\n\n#if (HAVE_QUIC_SSL_LUA_YIELD_PATCH && NGX_HTTP_V3)\nvoid ngx_http_lua_resume_quic_ssl_handshake(ngx_connection_t *c);\n#endif\n\n\nstatic ngx_inline void\nngx_http_lua_init_ctx(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx)\n{\n    ngx_memzero(ctx, sizeof(ngx_http_lua_ctx_t));\n    ctx->ctx_ref = LUA_NOREF;\n    ctx->entry_co_ctx.co_ref = LUA_NOREF;\n    ctx->entry_co_ctx.next_zombie_child_thread =\n        &ctx->entry_co_ctx.zombie_child_threads;\n    ctx->resume_handler = ngx_http_lua_wev_handler;\n    ctx->request = r;\n}\n\n\nstatic ngx_inline ngx_http_lua_ctx_t *\nngx_http_lua_create_ctx(ngx_http_request_t *r)\n{\n    ngx_int_t                    rc;\n    lua_State                   *L = NULL;\n    ngx_http_lua_ctx_t          *ctx;\n    ngx_pool_cleanup_t          *cln;\n    ngx_http_lua_loc_conf_t     *llcf;\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    ctx = ngx_palloc(r->pool, sizeof(ngx_http_lua_ctx_t));\n    if (ctx == NULL) {\n        return NULL;\n    }\n\n    ngx_http_lua_init_ctx(r, ctx);\n    ngx_http_set_ctx(r, ctx, ngx_http_lua_module);\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n    if (!llcf->enable_code_cache && r->connection->fd != (ngx_socket_t) -1) {\n        lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n#ifdef DDEBUG\n        dd(\"lmcf: %p\", lmcf);\n#endif\n\n        rc = ngx_http_lua_init_vm(&L, lmcf->lua, lmcf->cycle, r->pool, lmcf,\n                                  r->connection->log, &cln);\n        if (rc != NGX_OK) {\n            if (rc == NGX_DECLINED) {\n                ngx_http_lua_assert(L != NULL);\n\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"failed to load the 'resty.core' module \"\n                              \"(https://github.com/openresty/lua-resty\"\n                              \"-core); ensure you are using an OpenResty \"\n                              \"release from https://openresty.org/en/\"\n                              \"download.html (reason: %s)\",\n                              lua_tostring(L, -1));\n\n            } else {\n                /* rc == NGX_ERROR */\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"failed to initialize Lua VM\");\n            }\n\n            return NULL;\n        }\n\n        /* rc == NGX_OK */\n\n        ngx_http_lua_assert(L != NULL);\n\n        if (lmcf->init_handler) {\n            if (lmcf->init_handler(r->connection->log, lmcf, L) != NGX_OK) {\n                /* an error happened */\n                return NULL;\n            }\n        }\n\n        ctx->vm_state = cln->data;\n\n    } else {\n        ctx->vm_state = NULL;\n    }\n\n    return ctx;\n}\n\n\nstatic ngx_inline lua_State *\nngx_http_lua_get_lua_vm(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx)\n{\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    if (ctx == NULL) {\n        ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    }\n\n    if (ctx && ctx->vm_state) {\n        return ctx->vm_state->vm;\n    }\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n#ifdef DDEBUG\n    dd(\"lmcf->lua: %p\", lmcf->lua);\n#endif\n\n    return lmcf->lua;\n}\n\n\n#ifndef OPENRESTY_LUAJIT\n#define ngx_http_lua_req_key  \"__ngx_req\"\n#endif\n\n\nstatic ngx_inline ngx_http_request_t *\nngx_http_lua_get_req(lua_State *L)\n{\n#ifdef OPENRESTY_LUAJIT\n    return lua_getexdata(L);\n#else\n    ngx_http_request_t    *r;\n\n    lua_getglobal(L, ngx_http_lua_req_key);\n    r = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    return r;\n#endif\n}\n\n\nstatic ngx_inline void\nngx_http_lua_set_req(lua_State *L, ngx_http_request_t *r)\n{\n#ifdef OPENRESTY_LUAJIT\n    lua_setexdata(L, (void *) r);\n#else\n    lua_pushlightuserdata(L, r);\n    lua_setglobal(L, ngx_http_lua_req_key);\n#endif\n}\n\n\nstatic ngx_inline void\nngx_http_lua_attach_co_ctx_to_L(lua_State *L, ngx_http_lua_co_ctx_t *coctx)\n{\n#ifdef HAVE_LUA_EXDATA2\n    lua_setexdata2(L, (void *) coctx);\n#endif\n}\n\n\n#ifndef OPENRESTY_LUAJIT\nstatic ngx_inline void\nngx_http_lua_get_globals_table(lua_State *L)\n{\n    lua_pushvalue(L, LUA_GLOBALSINDEX);\n}\n\n\nstatic ngx_inline void\nngx_http_lua_set_globals_table(lua_State *L)\n{\n    lua_replace(L, LUA_GLOBALSINDEX);\n}\n#endif /* OPENRESTY_LUAJIT */\n\n\nstatic ngx_inline ngx_uint_t\nngx_http_lua_hash_str(u_char *src, size_t n)\n{\n    ngx_uint_t  key;\n\n    key = 0;\n\n    while (n--) {\n        key = ngx_hash(key, *src);\n        src++;\n    }\n\n    return key;\n}\n\n\nstatic ngx_inline ngx_int_t\nngx_http_lua_set_content_type(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx)\n{\n    ngx_http_lua_loc_conf_t     *llcf;\n\n    ctx->mime_set = 1;\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n    if (llcf->use_default_type\n        && r->headers_out.status != NGX_HTTP_NOT_MODIFIED)\n    {\n        return ngx_http_set_content_type(r);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_inline void\nngx_http_lua_cleanup_pending_operation(ngx_http_lua_co_ctx_t *coctx)\n{\n    if (coctx->cleanup) {\n        coctx->cleanup(coctx);\n        coctx->cleanup = NULL;\n    }\n}\n\n\nstatic ngx_inline ngx_chain_t *\nngx_http_lua_get_flush_chain(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx)\n{\n    ngx_chain_t  *cl;\n\n    cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,\n                                         &ctx->free_bufs, 0);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf->flush = 1;\n\n    return cl;\n}\n\n\n#if (nginx_version < 1011002)\nstatic ngx_inline in_port_t\nngx_inet_get_port(struct sockaddr *sa)\n{\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin6;\n#endif\n\n    switch (sa->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) sa;\n        return ntohs(sin6->sin6_port);\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    case AF_UNIX:\n        return 0;\n#endif\n\n    default: /* AF_INET */\n        sin = (struct sockaddr_in *) sa;\n        return ntohs(sin->sin_port);\n    }\n}\n#endif\n\n\nstatic ngx_inline ngx_int_t\nngx_http_lua_check_unsafe_uri_bytes(ngx_http_request_t *r, u_char *str,\n    size_t len, u_char *byte)\n{\n    size_t           i;\n    u_char           c;\n\n                     /* %00-%08, %0A-%1F, %7F */\n\n    static uint32_t  unsafe[] = {\n        0xfffffdff, /* 1111 1111 1111 1111  1111 1101 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */\n\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n        0x00000000  /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n    };\n\n    for (i = 0; i < len; i++, str++) {\n        c = *str;\n        if (unsafe[c >> 5] & (1 << (c & 0x1f))) {\n            *byte = c;\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_inline void\nngx_http_lua_free_thread(ngx_http_request_t *r, lua_State *L, int co_ref,\n    lua_State *co, ngx_http_lua_main_conf_t *lmcf)\n{\n#ifdef HAVE_LUA_RESETTHREAD\n    ngx_queue_t                 *q;\n    ngx_http_lua_thread_ref_t   *tref;\n    ngx_http_lua_ctx_t          *ctx;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP,\n                   r == NULL ? ngx_cycle->log : r->connection->log, 0,\n                   \"lua freeing light thread %p (ref %d)\", co, co_ref);\n\n    ctx = r != NULL ? ngx_http_get_module_ctx(r, ngx_http_lua_module) : NULL;\n    if (ctx != NULL\n        && L == ctx->entry_co_ctx.co\n        && L == lmcf->lua\n        && !ngx_queue_empty(&lmcf->free_lua_threads))\n    {\n        lua_resetthread(L, co);\n\n        q = ngx_queue_head(&lmcf->free_lua_threads);\n        tref = ngx_queue_data(q, ngx_http_lua_thread_ref_t, queue);\n\n        ngx_http_lua_assert(tref->ref == LUA_NOREF);\n        ngx_http_lua_assert(tref->co == NULL);\n\n        tref->ref = co_ref;\n        tref->co = co;\n\n        ngx_queue_remove(q);\n        ngx_queue_insert_head(&lmcf->cached_lua_threads, q);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP,\n                       r != NULL ? r->connection->log : ngx_cycle->log, 0,\n                       \"lua caching unused lua thread %p (ref %d)\", co,\n                       co_ref);\n\n        return;\n    }\n#endif\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP,\n                   r != NULL ? r->connection->log : ngx_cycle->log, 0,\n                   \"lua unref lua thread %p (ref %d)\", co, co_ref);\n\n    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                          coroutines_key));\n    lua_rawget(L, LUA_REGISTRYINDEX);\n\n    luaL_unref(L, -1, co_ref);\n    lua_pop(L, 1);\n}\n\n\nstatic ngx_inline int\nngx_http_lua_new_cached_thread(lua_State *L, lua_State **out_co,\n    ngx_http_lua_main_conf_t *lmcf, int set_globals)\n{\n    int                          co_ref;\n    lua_State                   *co;\n\n#ifdef HAVE_LUA_RESETTHREAD\n    ngx_queue_t                 *q;\n    ngx_http_lua_thread_ref_t   *tref;\n\n    if (L == lmcf->lua && !ngx_queue_empty(&lmcf->cached_lua_threads)) {\n        q = ngx_queue_head(&lmcf->cached_lua_threads);\n        tref = ngx_queue_data(q, ngx_http_lua_thread_ref_t, queue);\n\n        ngx_http_lua_assert(tref->ref != LUA_NOREF);\n        ngx_http_lua_assert(tref->co != NULL);\n\n        co = tref->co;\n        co_ref = tref->ref;\n\n        tref->co = NULL;\n        tref->ref = LUA_NOREF;\n\n        ngx_queue_remove(q);\n        ngx_queue_insert_head(&lmcf->free_lua_threads, q);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua reusing cached lua thread %p (ref %d)\", co, co_ref);\n\n        lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                              coroutines_key));\n        lua_rawget(L, LUA_REGISTRYINDEX);\n        lua_rawgeti(L, -1, co_ref);\n\n    } else\n#endif\n    {\n        lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(\n                              coroutines_key));\n        lua_rawget(L, LUA_REGISTRYINDEX);\n        co = lua_newthread(L);\n        lua_pushvalue(L, -1);\n        co_ref = luaL_ref(L, -3);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"lua ref lua thread %p (ref %d)\", co, co_ref);\n\n#ifndef OPENRESTY_LUAJIT\n        if (set_globals) {\n            lua_createtable(co, 0, 0);  /* the new globals table */\n\n            /* co stack: global_tb */\n\n            lua_createtable(co, 0, 1);  /* the metatable */\n            ngx_http_lua_get_globals_table(co);\n            lua_setfield(co, -2, \"__index\");\n            lua_setmetatable(co, -2);\n\n            /* co stack: global_tb */\n\n            ngx_http_lua_set_globals_table(co);\n        }\n#endif\n    }\n\n    *out_co = co;\n\n    return co_ref;\n}\n\n\nstatic ngx_inline void *\nngx_http_lua_hash_find_lc(ngx_hash_t *hash, ngx_uint_t key, u_char *name,\n    size_t len)\n{\n    ngx_uint_t       i;\n    ngx_hash_elt_t  *elt;\n\n    elt = hash->buckets[key % hash->size];\n\n    if (elt == NULL) {\n        return NULL;\n    }\n\n    while (elt->value) {\n        if (len != (size_t) elt->len) {\n            goto next;\n        }\n\n        for (i = 0; i < len; i++) {\n            if (ngx_tolower(name[i]) != elt->name[i]) {\n                goto next;\n            }\n        }\n\n        return elt->value;\n\n    next:\n\n        elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,\n                                               sizeof(void *));\n        continue;\n    }\n\n    return NULL;\n}\n\n\nextern ngx_uint_t  ngx_http_lua_location_hash;\nextern ngx_uint_t  ngx_http_lua_content_length_hash;\n\n\n#endif /* _NGX_HTTP_LUA_UTIL_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_variable.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_util.h\"\n\n\nint\nngx_http_lua_ffi_var_get(ngx_http_request_t *r, u_char *name_data,\n    size_t name_len, u_char *lowcase_buf, int capture_id, u_char **value,\n    size_t *value_len, char **err)\n{\n    ngx_uint_t                   hash;\n    ngx_str_t                    name;\n    ngx_http_variable_value_t   *vv;\n\n#if (NGX_PCRE)\n    u_char                      *p;\n    ngx_uint_t                   n;\n    int                         *cap;\n#endif\n\n    if (r == NULL) {\n        *err = \"no request object found\";\n        return NGX_ERROR;\n    }\n\n    if ((r)->connection->fd == (ngx_socket_t) -1) {\n        *err = \"API disabled in the current context\";\n        return NGX_ERROR;\n    }\n\n#if (NGX_PCRE)\n    if (name_data == 0) {\n        if (capture_id <= 0) {\n            return NGX_DECLINED;\n        }\n\n        /* it is a regex capturing variable */\n\n        n = (ngx_uint_t) capture_id * 2;\n\n        dd(\"n = %d, ncaptures = %d\", (int) n, (int) r->ncaptures);\n\n        if (r->captures == NULL\n            || r->captures_data == NULL\n            || n >= r->ncaptures)\n        {\n            return NGX_DECLINED;\n        }\n\n        /* n >= 0 && n < r->ncaptures */\n\n        cap = r->captures;\n        p = r->captures_data;\n\n        *value = &p[cap[n]];\n        *value_len = (size_t) (cap[n + 1] - cap[n]);\n\n        return NGX_OK;\n    }\n#endif\n\n#if (NGX_HTTP_V3)\n    if (name_len == 9\n        && r->http_version == NGX_HTTP_VERSION_30\n        && ngx_strncasecmp(name_data, (u_char *) \"http_host\", 9) == 0\n        && r->headers_in.server.data != NULL)\n    {\n        *value = r->headers_in.server.data;\n        *value_len = r->headers_in.server.len;\n        return NGX_OK;\n    }\n#endif\n\n    hash = ngx_hash_strlow(lowcase_buf, name_data, name_len);\n\n    name.data = lowcase_buf;\n    name.len = name_len;\n\n    dd(\"variable name: %.*s\", (int) name_len, lowcase_buf);\n\n    vv = ngx_http_get_variable(r, &name, hash);\n    if (vv == NULL || vv->not_found) {\n        return NGX_DECLINED;\n    }\n\n    *value = vv->data;\n    *value_len = vv->len;\n    return NGX_OK;\n}\n\n\nint\nngx_http_lua_ffi_var_set(ngx_http_request_t *r, u_char *name_data,\n    size_t name_len, u_char *lowcase_buf, u_char *value, size_t value_len,\n    u_char *errbuf, size_t *errlen)\n{\n    u_char                      *p;\n    ngx_uint_t                   hash;\n    ngx_http_variable_t         *v;\n    ngx_http_variable_value_t   *vv;\n    ngx_http_core_main_conf_t   *cmcf;\n\n    if (r == NULL) {\n        *errlen = ngx_snprintf(errbuf, *errlen, \"no request object found\")\n                  - errbuf;\n        return NGX_ERROR;\n    }\n\n    if ((r)->connection->fd == (ngx_socket_t) -1) {\n        *errlen = ngx_snprintf(errbuf, *errlen,\n                               \"API disabled in the current context\")\n                  - errbuf;\n        return NGX_ERROR;\n    }\n\n    hash = ngx_hash_strlow(lowcase_buf, name_data, name_len);\n\n    dd(\"variable name: %.*s\", (int) name_len, lowcase_buf);\n\n    /* we fetch the variable itself */\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    v = ngx_hash_find(&cmcf->variables_hash, hash, lowcase_buf, name_len);\n\n    if (v) {\n        if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {\n            dd(\"variable not changeable\");\n            *errlen = ngx_snprintf(errbuf, *errlen,\n                                   \"variable \\\"%*s\\\" not changeable\",\n                                   name_len, lowcase_buf)\n                      - errbuf;\n            return NGX_ERROR;\n        }\n\n        if (v->set_handler) {\n\n            dd(\"set variables with set_handler\");\n\n            if (value != NULL && value_len) {\n                vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t)\n                                + value_len);\n                if (vv == NULL) {\n                    goto nomem;\n                }\n\n                p = (u_char *) vv + sizeof(ngx_http_variable_value_t);\n                ngx_memcpy(p, value, value_len);\n                value = p;\n\n            } else {\n                vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));\n                if (vv == NULL) {\n                    goto nomem;\n                }\n            }\n\n            if (value == NULL) {\n                vv->valid = 0;\n                vv->not_found = 1;\n                vv->no_cacheable = 0;\n                vv->data = NULL;\n                vv->len = 0;\n\n            } else {\n                vv->valid = 1;\n                vv->not_found = 0;\n                vv->no_cacheable = 0;\n\n                vv->data = value;\n                vv->len = value_len;\n            }\n\n            v->set_handler(r, vv, v->data);\n            return NGX_OK;\n        }\n\n        if (v->flags & NGX_HTTP_VAR_INDEXED) {\n            vv = &r->variables[v->index];\n\n            dd(\"set indexed variable\");\n\n            if (value == NULL) {\n                vv->valid = 0;\n                vv->not_found = 1;\n                vv->no_cacheable = 0;\n\n                vv->data = NULL;\n                vv->len = 0;\n\n            } else {\n                p = ngx_palloc(r->pool, value_len);\n                if (p == NULL) {\n                    goto nomem;\n                }\n\n                ngx_memcpy(p, value, value_len);\n                value = p;\n\n                vv->valid = 1;\n                vv->not_found = 0;\n                vv->no_cacheable = 0;\n\n                vv->data = value;\n                vv->len = value_len;\n            }\n\n            return NGX_OK;\n        }\n\n        *errlen = ngx_snprintf(errbuf, *errlen,\n                               \"variable \\\"%*s\\\" cannot be assigned \"\n                               \"a value\", name_len, lowcase_buf)\n                  - errbuf;\n        return NGX_ERROR;\n    }\n\n    /* variable not found */\n\n    *errlen = ngx_snprintf(errbuf, *errlen,\n                           \"variable \\\"%*s\\\" not found for writing; \"\n                           \"maybe it is a built-in variable that is not \"\n                           \"changeable or you forgot to use \\\"set $%*s '';\\\" \"\n                           \"in the config file to define it first\",\n                           name_len, lowcase_buf, name_len, lowcase_buf)\n              - errbuf;\n    return NGX_ERROR;\n\nnomem:\n\n    *errlen = ngx_snprintf(errbuf, *errlen, \"no memory\") - errbuf;\n    return NGX_ERROR;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_worker.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n#if !(NGX_WIN32)\n#include <ngx_channel.h>\n#endif\n\n\n#define NGX_PROCESS_PRIVILEGED_AGENT    99\n\n\nint\nngx_http_lua_ffi_worker_pid(void)\n{\n    return (int) ngx_pid;\n}\n\n\n#if !(NGX_WIN32)\nint\nngx_http_lua_ffi_worker_pids(int *pids, size_t *pids_len)\n{\n    size_t    n;\n    ngx_int_t i;\n\n    n = 0;\n    for (i = 0; n < *pids_len && i < NGX_MAX_PROCESSES; i++) {\n        if (i != ngx_process_slot && ngx_processes[i].pid == 0) {\n            break;\n        }\n\n        /* The current process */\n        if (i == ngx_process_slot) {\n            pids[n++] = ngx_pid;\n        }\n\n        if (ngx_processes[i].channel[0] > 0 && ngx_processes[i].pid > 0) {\n            pids[n++] = ngx_processes[i].pid;\n        }\n    }\n\n    if (n == 0) {\n        return NGX_ERROR;\n    }\n\n    *pids_len = n;\n\n    return NGX_OK;\n}\n#endif\n\n\nint\nngx_http_lua_ffi_worker_id(void)\n{\n#if (nginx_version >= 1009001)\n    if (ngx_process != NGX_PROCESS_WORKER\n        && ngx_process != NGX_PROCESS_SINGLE)\n    {\n        return -1;\n    }\n\n    return (int) ngx_worker;\n#else\n    return -1;\n#endif\n}\n\n\nint\nngx_http_lua_ffi_worker_exiting(void)\n{\n    return (int) ngx_exiting;\n}\n\n\nint\nngx_http_lua_ffi_worker_count(void)\n{\n    ngx_core_conf_t   *ccf;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,\n                                           ngx_core_module);\n\n    return (int) ccf->worker_processes;\n}\n\n\nint\nngx_http_lua_ffi_master_pid(void)\n{\n#if (nginx_version >= 1013008)\n    if (ngx_process == NGX_PROCESS_SINGLE) {\n        return (int) ngx_pid;\n    }\n\n    return (int) ngx_parent;\n#else\n    return NGX_ERROR;\n#endif\n}\n\n\nint\nngx_http_lua_ffi_get_process_type(void)\n{\n    ngx_core_conf_t  *ccf;\n\n#if defined(HAVE_PRIVILEGED_PROCESS_PATCH) && !NGX_WIN32\n    if (ngx_process == NGX_PROCESS_HELPER) {\n        if (ngx_is_privileged_agent) {\n            return NGX_PROCESS_PRIVILEGED_AGENT;\n        }\n    }\n#endif\n\n    if (ngx_process == NGX_PROCESS_SINGLE) {\n        ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,\n                                               ngx_core_module);\n\n        if (ccf->master) {\n            return NGX_PROCESS_MASTER;\n        }\n    }\n\n    return ngx_process;\n}\n\n#if defined(nginx_version) && nginx_version >= 1019003\nint\nngx_http_lua_ffi_enable_privileged_agent(char **err, unsigned int connections)\n#else\nint\nngx_http_lua_ffi_enable_privileged_agent(char **err)\n#endif\n{\n#ifdef HAVE_PRIVILEGED_PROCESS_PATCH\n    ngx_core_conf_t   *ccf;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,\n                                           ngx_core_module);\n\n    ccf->privileged_agent = 1;\n#if defined(nginx_version) && nginx_version >= 1019003\n    ccf->privileged_agent_connections = connections;\n#endif\n\n    return NGX_OK;\n\n#else\n    *err = \"missing privileged agent process patch in the nginx core\";\n    return NGX_ERROR;\n#endif\n}\n\n\nvoid\nngx_http_lua_ffi_process_signal_graceful_exit(void)\n{\n    ngx_quit = 1;\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_worker_thread.c",
    "content": "/*\n * Copyright (C) Yichun Zhang (agentzh)\n * Copyright (C) Jinhua Luo (kingluo)\n * I hereby assign copyright in this code to the lua-nginx-module project,\n * to be licensed under the same terms as the rest of the code.\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_lua_worker_thread.h\"\n#include \"ngx_http_lua_util.h\"\n#include \"ngx_http_lua_string.h\"\n#include \"ngx_http_lua_config.h\"\n#include \"ngx_http_lua_shdict.h\"\n\n#ifndef STRINGIFY\n#define TOSTRING(x)  #x\n#define STRINGIFY(x) TOSTRING(x)\n#endif\n\n#if (NGX_THREADS)\n\n\n#include <ngx_thread.h>\n#include <ngx_thread_pool.h>\n\n#define LUA_COPY_MAX_DEPTH 100\n\ntypedef struct ngx_http_lua_task_ctx_s {\n    lua_State                        *vm;\n    struct ngx_http_lua_task_ctx_s   *next;\n} ngx_http_lua_task_ctx_t;\n\n\ntypedef struct {\n    ngx_http_lua_task_ctx_t *ctx;\n    ngx_http_lua_co_ctx_t   *wait_co_ctx;\n    int                      n_args;\n    int                      rc;\n    ngx_uint_t               is_abort:1;\n} ngx_http_lua_worker_thread_ctx_t;\n\n\nstatic  ngx_http_lua_task_ctx_t   dummy_ctx;\nstatic  ngx_http_lua_task_ctx_t  *ctxpool = &dummy_ctx;\nstatic  ngx_uint_t                worker_thread_vm_count;\n\n\nvoid\nngx_http_lua_thread_exit_process(void)\n{\n    ngx_http_lua_task_ctx_t  *ctx;\n\n    while (ctxpool->next != NULL) {\n        ctx = ctxpool->next;\n        ctxpool->next = ctx->next;\n        lua_close(ctx->vm);\n        ngx_free(ctx);\n    }\n}\n\n\n/*\n * Re-implement ngx_thread_task_alloc to avoid alloc from request pool\n * since the request may exit before worker thread finish.\n * And we may implement a memory pool for this allocation in the future\n * to avoid memory fragmentation.\n */\nstatic ngx_thread_task_t *\nngx_http_lua_thread_task_alloc(size_t size)\n{\n    ngx_thread_task_t  *task;\n\n    task = ngx_calloc(sizeof(ngx_thread_task_t) + size, ngx_cycle->log);\n    if (task == NULL) {\n        return NULL;\n    }\n\n    task->ctx = task + 1;\n\n    return task;\n}\n\n\nstatic void\nngx_http_lua_thread_task_free(void *ctx)\n{\n    ngx_thread_task_t *task = ctx;\n    ngx_free(task - 1);\n}\n\n\nstatic ngx_http_lua_task_ctx_t *\nngx_http_lua_get_task_ctx(lua_State *L, ngx_http_request_t *r)\n{\n    ngx_http_lua_task_ctx_t *ctx = NULL;\n\n    size_t           path_len;\n    const char      *path;\n    size_t           cpath_len;\n    const char      *cpath;\n    lua_State       *vm;\n\n    ngx_http_lua_main_conf_t    *lmcf;\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    if (ctxpool->next == NULL) {\n        if (worker_thread_vm_count >= lmcf->worker_thread_vm_pool_size) {\n            return NULL;\n        }\n\n        ctx = ngx_calloc(sizeof(ngx_http_lua_task_ctx_t), ngx_cycle->log);\n        if (ctx == NULL) {\n            return NULL;\n        }\n\n        vm = luaL_newstate();\n\n        if (vm == NULL) {\n            ngx_free(ctx);\n            return NULL;\n        }\n\n        worker_thread_vm_count++;\n\n        ctx->vm = vm;\n\n        luaL_openlibs(vm);\n\n        /* copy package.path and package.cpath */\n        lua_getglobal(L, \"package\");\n        lua_getfield(L, -1, \"path\");\n        path = lua_tolstring(L, -1, &path_len);\n        lua_getfield(L, -2, \"cpath\");\n        cpath = lua_tolstring(L, -1, &cpath_len);\n\n        lua_getglobal(vm, \"package\");\n        lua_pushlstring(vm, path, path_len);\n        lua_setfield(vm, -2, \"path\");\n        lua_pushlstring(vm, cpath, cpath_len);\n        lua_setfield(vm, -2, \"cpath\");\n        lua_pop(vm, 1);\n\n        /* pop path, cpath and \"package\" table from L */\n        lua_pop(L, 3);\n\n        /* inject API from C */\n        lua_newtable(vm);    /* ngx.* */\n        ngx_http_lua_inject_string_api(vm);\n        ngx_http_lua_inject_config_api(vm);\n        ngx_http_lua_inject_shdict_api(lmcf, vm);\n        lua_setglobal(vm, \"ngx\");\n\n        lua_getglobal(vm, \"require\");\n        lua_pushstring(vm, \"resty.core.hash\");\n        if (lua_pcall(vm, 1, 0, 0) != 0) {\n            lua_close(vm);\n            ngx_free(ctx);\n            return NULL;\n        }\n\n        lua_getglobal(vm, \"require\");\n        lua_pushstring(vm, \"resty.core.base64\");\n        if (lua_pcall(vm, 1, 0, 0) != 0) {\n            lua_close(vm);\n            ngx_free(ctx);\n            return NULL;\n        }\n\n        lua_getglobal(vm, \"require\");\n        lua_pushstring(vm, \"resty.core.shdict\");\n        if (lua_pcall(vm, 1, 0, 0) != 0) {\n            lua_close(vm);\n            ngx_free(ctx);\n            return NULL;\n        }\n\n    } else {\n        ctx = ctxpool->next;\n        ctxpool->next = ctx->next;\n        ctx->next = NULL;\n    }\n\n    return ctx;\n}\n\n\nstatic void\nngx_http_lua_free_task_ctx(ngx_http_lua_task_ctx_t *ctx)\n{\n    lua_State   *vm;\n\n    ctx->next = ctxpool->next;\n    ctxpool->next = ctx;\n\n    /* clean Lua stack */\n    vm = ctx->vm;\n\n    /* call collectgarbage(\"collect\") */\n    lua_settop(vm, 0);\n    lua_getglobal(vm, \"collectgarbage\");\n    lua_pushstring(vm, \"collect\");\n    lua_pcall(vm, 1, 1, 0);\n\n    lua_settop(vm, 0);\n}\n\n\nstatic int\nngx_http_lua_xcopy(lua_State *from, lua_State *to, int idx,\n    const int allow_nil, const int depth, const char **err)\n{\n    size_t           len = 0;\n    const char      *str;\n    int              typ;\n    int              top_from, top_to;\n\n    typ = lua_type(from, idx);\n    switch (typ) {\n    case LUA_TBOOLEAN:\n        lua_pushboolean(to, lua_toboolean(from, idx));\n        return LUA_TBOOLEAN;\n\n    case LUA_TLIGHTUSERDATA:\n        lua_pushlightuserdata(to, lua_touserdata(from, idx));\n        return LUA_TLIGHTUSERDATA;\n\n    case LUA_TNUMBER:\n        lua_pushnumber(to, lua_tonumber(from, idx));\n        return LUA_TNUMBER;\n\n    case LUA_TSTRING:\n        str = lua_tolstring(from, idx, &len);\n        lua_pushlstring(to, str, len);\n        return LUA_TSTRING;\n\n    case LUA_TTABLE:\n        if (depth >= LUA_COPY_MAX_DEPTH) {\n            *err = \"suspicious circular references, \"\n                   \"table depth exceed max depth: \"\n                   STRINGIFY(LUA_COPY_MAX_DEPTH);\n            return LUA_TNONE;\n        }\n\n        top_from = lua_gettop(from);\n        top_to = lua_gettop(to);\n\n        lua_newtable(to);\n\n        /* to positive number */\n        if (idx < 0) {\n            idx = lua_gettop(from) + idx + 1;\n        }\n\n        lua_pushnil(from);\n\n        while (lua_next(from, idx) != 0) {\n            if (ngx_http_lua_xcopy(from, to, -2, 0, depth + 1, err) != LUA_TNONE\n                && ngx_http_lua_xcopy(from, to, -1, 0,\n                                      depth + 1, err) != LUA_TNONE)\n            {\n                lua_rawset(to, -3);\n\n            } else {\n                lua_settop(from, top_from);\n                lua_settop(to, top_to);\n                return LUA_TNONE;\n            }\n\n            lua_pop(from, 1);\n        }\n\n        return LUA_TTABLE;\n\n    case LUA_TNIL:\n        if (allow_nil) {\n            lua_pushnil(to);\n            return LUA_TNIL;\n        }\n\n        *err = \"unsupported Lua type: LUA_TNIL\";\n        return LUA_TNONE;\n\n    case LUA_TFUNCTION:\n        *err = \"unsupported Lua type: LUA_TFUNCTION\";\n        return LUA_TNONE;\n\n    case LUA_TUSERDATA:\n        *err = \"unsupported Lua type: LUA_TUSERDATA\";\n        return LUA_TNONE;\n\n    case LUA_TTHREAD:\n        *err = \"unsupported Lua type: LUA_TTHREAD\";\n        return LUA_TNONE;\n\n    default:\n        *err = \"unsupported Lua type\";\n        return LUA_TNONE;\n    }\n}\n\n\n/* executed in a separate thread */\nstatic void\nngx_http_lua_worker_thread_handler(void *data, ngx_log_t *log)\n{\n    ngx_http_lua_worker_thread_ctx_t     *ctx = data;\n    lua_State                            *vm = ctx->ctx->vm;\n\n    /* function + args in the lua stack */\n    ngx_http_lua_assert(lua_gettop(vm) == ctx->n_args + 1);\n\n    ctx->rc = lua_pcall(vm, ctx->n_args, LUA_MULTRET, 0);\n}\n\n\nstatic ngx_int_t\nngx_http_lua_worker_thread_resume(ngx_http_request_t *r)\n{\n    lua_State                   *vm;\n    ngx_connection_t            *c;\n    ngx_int_t                    rc;\n    ngx_uint_t                   nreqs;\n    ngx_http_lua_ctx_t          *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->resume_handler = ngx_http_lua_wev_handler;\n\n    c = r->connection;\n    vm = ngx_http_lua_get_lua_vm(r, ctx);\n    nreqs = c->requests;\n\n    rc = ngx_http_lua_run_thread(vm, r, ctx,\n                                 ctx->cur_co_ctx->nresults_from_worker_thread);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"lua run thread returned %d\", rc);\n\n    if (rc == NGX_AGAIN) {\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_http_lua_finalize_request(r, NGX_DONE);\n        return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);\n    }\n\n    if (ctx->entered_content_phase) {\n        ngx_http_lua_finalize_request(r, rc);\n        return NGX_DONE;\n    }\n\n    return rc;\n}\n\n\n/* executed in nginx event loop */\nstatic void\nngx_http_lua_worker_thread_event_handler(ngx_event_t *ev)\n{\n    ngx_http_lua_worker_thread_ctx_t *worker_thread_ctx;\n    lua_State                        *L;\n    ngx_http_request_t               *r;\n    ngx_connection_t                 *c;\n    int                               nresults;\n    size_t                            len;\n    const char                       *str;\n    int                               i;\n    ngx_http_lua_ctx_t               *ctx;\n    lua_State                        *vm;\n    int                               saved_top;\n    const char                       *err;\n\n    worker_thread_ctx = ev->data;\n\n    if (worker_thread_ctx->is_abort) {\n        goto failed;\n    }\n\n    L = worker_thread_ctx->wait_co_ctx->co;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        goto failed;\n    }\n\n    c = r->connection;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        goto failed;\n    }\n\n    vm = worker_thread_ctx->ctx->vm;\n\n    if (worker_thread_ctx->rc != 0) {\n        str = lua_tolstring(vm, 1, &len);\n        lua_pushboolean(L, 0);\n        lua_pushlstring(L, str, len);\n        nresults = 2;\n\n    } else {\n        /* copying return values */\n        saved_top = lua_gettop(L);\n        lua_pushboolean(L, 1);\n        nresults = lua_gettop(vm) + 1;\n        for (i = 1; i < nresults; i++) {\n            err = NULL;\n            if (ngx_http_lua_xcopy(vm, L, i, 1, 1, &err) == LUA_TNONE) {\n                lua_settop(L, saved_top);\n                lua_pushboolean(L, 0);\n                lua_pushfstring(L, \"%s in the return value\",\n                                err != NULL ? err : \"unsupoorted Lua type\");\n                nresults = 2;\n                break;\n            }\n        }\n    }\n\n    ctx->cur_co_ctx = worker_thread_ctx->wait_co_ctx;\n    ctx->cur_co_ctx->nresults_from_worker_thread = nresults;\n    ctx->cur_co_ctx->cleanup = NULL;\n\n    ngx_http_lua_free_task_ctx(worker_thread_ctx->ctx);\n    ngx_http_lua_thread_task_free(worker_thread_ctx);\n\n    /* resume the caller coroutine */\n\n    if (ctx->entered_content_phase) {\n        (void) ngx_http_lua_worker_thread_resume(r);\n\n    } else {\n        ctx->resume_handler = ngx_http_lua_worker_thread_resume;\n        ngx_http_core_run_phases(r);\n    }\n\n    ngx_http_run_posted_requests(c);\n\n    return;\n\nfailed:\n\n    ngx_http_lua_free_task_ctx(worker_thread_ctx->ctx);\n    ngx_http_lua_thread_task_free(worker_thread_ctx);\n    return;\n}\n\n\nstatic void\nngx_http_lua_worker_thread_cleanup(void *data)\n{\n    ngx_http_lua_co_ctx_t *ctx                          = data;\n    ngx_http_lua_worker_thread_ctx_t *worker_thread_ctx = ctx->data;\n    worker_thread_ctx->is_abort                         = 1;\n}\n\n\n/* It's not easy to use pure ffi here, using the Lua C API for now. */\nstatic int\nngx_http_lua_run_worker_thread(lua_State *L)\n{\n    ngx_http_request_t                 *r;\n    ngx_http_lua_ctx_t                 *ctx;\n    int                                 n_args;\n    ngx_str_t                           thread_pool_name;\n    ngx_thread_pool_t                  *thread_pool;\n    ngx_http_lua_task_ctx_t            *tctx;\n    lua_State                          *vm;\n    size_t                              len;\n    const char                         *mod_name;\n    const char                         *func_name;\n    int                                 rc;\n    const char                         *err;\n    int                                 i;\n    ngx_thread_task_t                  *task;\n    ngx_http_lua_worker_thread_ctx_t   *worker_thread_ctx;\n\n    r = ngx_http_lua_get_req(L);\n    if (r == NULL) {\n        return luaL_error(L, \"no request found\");\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);\n    if (ctx == NULL) {\n        return luaL_error(L, \"no ctx found\");\n    }\n\n    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE);\n\n    n_args = lua_gettop(L);\n\n    if (n_args < 3) {\n        lua_pushboolean(L, 0);\n        lua_pushstring(L, \"expecting at least 3 arguments\");\n        return 2;\n    }\n\n    thread_pool_name.data = (u_char *)\n                            lua_tolstring(L, 1, &thread_pool_name.len);\n\n    if (thread_pool_name.data == NULL) {\n        lua_pushboolean(L, 0);\n        lua_pushstring(L, \"threadpool should be a string\");\n        return 2;\n    }\n\n    thread_pool = ngx_thread_pool_get((ngx_cycle_t *) ngx_cycle,\n                                      &thread_pool_name);\n\n    if (thread_pool == NULL) {\n        lua_pushboolean(L, 0);\n        lua_pushfstring(L, \"thread pool %s not found\", thread_pool_name.data);\n        return 2;\n    }\n\n    mod_name = lua_tolstring(L, 2, &len);\n    if (mod_name == NULL) {\n        lua_pushboolean(L, 0);\n        lua_pushstring(L, \"module name should be a string\");\n        return 2;\n    }\n\n    func_name = lua_tolstring(L, 3, NULL);\n    if (func_name == NULL) {\n        lua_pushboolean(L, 0);\n        lua_pushstring(L, \"function name should be a string\");\n        return 2;\n    }\n\n    /* get vm */\n    tctx = ngx_http_lua_get_task_ctx(L, r);\n    if (tctx == NULL) {\n        lua_pushboolean(L, 0);\n        lua_pushstring(L, \"no available Lua vm\");\n        return 2;\n    }\n\n    vm = tctx->vm;\n\n    ngx_http_lua_assert(lua_gettop(vm) == 0);\n\n    /* push function from module require */\n    lua_getfield(vm, LUA_GLOBALSINDEX, \"require\");\n    lua_pushlstring(vm, mod_name, len);\n    rc = lua_pcall(vm, 1, 1, 0);\n\n    if (rc != 0) {\n        err = lua_tolstring(vm, 1, &len);\n        lua_pushboolean(L, 0);\n        lua_pushlstring(L, err, len);\n        ngx_http_lua_free_task_ctx(tctx);\n        return 2;\n    }\n\n    if (!lua_istable(vm, -1)) {\n        lua_pushboolean(L, 0);\n        lua_pushstring(L, \"invalid lua module\");\n        ngx_http_lua_free_task_ctx(tctx);\n        return 2;\n    }\n\n    lua_getfield(vm, -1, func_name);\n    if (!lua_isfunction(vm, -1)) {\n        lua_pushboolean(L, 0);\n        lua_pushstring(L, \"invalid function\");\n        ngx_http_lua_free_task_ctx(tctx);\n        return 2;\n    }\n\n    /* remove the table returned by require */\n    lua_remove(vm, 1);\n\n    /* copying passed arguments */\n    for (i = 4; i <= n_args; i++) {\n        err = NULL;\n        if (ngx_http_lua_xcopy(L, vm, i, 1, 1, &err) == LUA_TNONE) {\n            lua_pushboolean(L, 0);\n            lua_pushfstring(L, \"%s in the argument\",\n                            err != NULL ? err : \"unsupoorted Lua type\");\n            ngx_http_lua_free_task_ctx(tctx);\n            return 2;\n        }\n    }\n\n    /* post task */\n    task = ngx_http_lua_thread_task_alloc(\n                sizeof(ngx_http_lua_worker_thread_ctx_t));\n\n    if (task == NULL) {\n        ngx_http_lua_free_task_ctx(tctx);\n        lua_pushboolean(L, 0);\n        lua_pushstring(L, \"no memory\");\n        return 2;\n    }\n\n    worker_thread_ctx = task->ctx;\n\n    worker_thread_ctx->ctx = tctx;\n    worker_thread_ctx->wait_co_ctx = ctx->cur_co_ctx;\n\n    ctx->cur_co_ctx->cleanup = ngx_http_lua_worker_thread_cleanup;\n    ctx->cur_co_ctx->data = worker_thread_ctx;\n\n    worker_thread_ctx->n_args = n_args - 3;\n    worker_thread_ctx->rc = 0;\n    worker_thread_ctx->is_abort = 0;\n\n    task->handler = ngx_http_lua_worker_thread_handler;\n    task->event.handler = ngx_http_lua_worker_thread_event_handler;\n    task->event.data = worker_thread_ctx;\n\n    if (ngx_thread_task_post(thread_pool, task) != NGX_OK) {\n        ngx_http_lua_free_task_ctx(tctx);\n        ngx_http_lua_thread_task_free(task);\n        lua_pushboolean(L, 0);\n        lua_pushstring(L, \"ngx_thread_task_post failed\");\n        return 2;\n    }\n\n    return lua_yield(L, 0);\n}\n\n\nvoid\nngx_http_lua_inject_worker_thread_api(ngx_log_t *log, lua_State *L)\n{\n    lua_pushcfunction(L, ngx_http_lua_run_worker_thread);\n    lua_setfield(L, -2, \"run_worker_thread\");\n}\n\n#endif\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "src/ngx_http_lua_worker_thread.h",
    "content": "/*\n * Copyright (C) Yichun Zhang (agentzh)\n * Copyright (C) Jinhua Luo (kingluo)\n * I hereby assign copyright in this code to the lua-nginx-module project,\n * to be licensed under the same terms as the rest of the code.\n */\n\n#ifndef _NGX_HTTP_LUA_WORKER_THREAD_H_INCLUDED_\n#define _NGX_HTTP_LUA_WORKER_THREAD_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_worker_thread_api(ngx_log_t *log, lua_State *L);\nvoid ngx_http_lua_thread_exit_process(void);\n\n\n#endif /* _NGX_HTTP_LUA_WORKER_THREAD_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "t/.gitignore",
    "content": "servroot\n\n"
  },
  {
    "path": "t/000--init.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n\nour $http_config = <<'_EOC_';\n    # lua-resty-string is required for lua-resty-mysql\n    lua_package_path \"../lua-resty-rsa/lib/?.lua;../lua-resty-mysql/lib/?.lua;../lua-resty-string/lib/?.lua;;\";\n_EOC_\n\nno_shuffle();\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: conv_uid - drop table\n--- http_config eval: $::http_config\n--- config\n    location = /init {\n        content_by_lua_block {\n            local mysql = require \"resty.mysql\"\n            local db = assert(mysql:new())\n            local ok, err, errcode, sqlstate = db:connect{\n                host = \"127.0.0.1\",\n                port = $TEST_NGINX_MYSQL_PORT,\n                database = \"ngx_test\",\n                user = \"ngx_test\",\n                password = \"ngx_test\",\n                charset = \"utf8\",\n            }\n\n            local queries = {\n                \"DROP TABLE IF EXISTS conv_uid\",\n                \"CREATE TABLE conv_uid(id serial primary key, new_uid integer, old_uid integer)\",\n                \"INSERT INTO conv_uid(old_uid,new_uid) VALUES(32,56),(35,78)\",\n            }\n\n            for _, query in ipairs(queries) do\n                local ok, err = db:query(query)\n                if not ok then\n                    ngx.say(\"failed to run mysql query \\\"\", query, \"\\\": \", err)\n                    return\n                end\n            end\n\n            ngx.say(\"done!\")\n        }\n    }\n--- request\nGET /init\n--- response_body\ndone!\n--- timeout: 10\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: flush data from memcached\n--- config\n    location /flush {\n        set $memc_cmd flush_all;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n--- request\nGET /flush\n--- error_code: 200\n--- response_body eval\n\"OK\\r\n\"\n--- timeout: 10\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/000-sanity.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => blocks() * repeat_each() * 2;\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity (integer)\n--- config\n    location /lua {\n        echo 2;\n    }\n--- request\nGET /lua\n--- response_body\n2\n\n\n\n=== TEST 2: sanity (string)\n--- config\n    location /lua {\n        echo \"helloworld\";\n    }\n--- request\nGET /lua\n--- response_body\nhelloworld\n"
  },
  {
    "path": "t/001-set.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 5);\n\n#log_level(\"warn\");\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple set (integer)\n--- config\n    location /lua {\n        set_by_lua $res \"return 1+1\";\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body\n2\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: simple set (string)\n--- config\n    location /lua {\n        set_by_lua $res \"return 'hello' .. 'world'\";\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body\nhelloworld\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: internal only\n--- config\n    location /lua {\n        set_by_lua $res \"local function fib(n) if n > 2 then return fib(n-1)+fib(n-2) else return 1 end end return fib(10)\";\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body\n55\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: inlined script with arguments\n--- config\n    location /lua {\n        set_by_lua $res \"return ngx.arg[1] + ngx.arg[2]\" $arg_a $arg_b;\n        echo $res;\n    }\n--- request\nGET /lua?a=1&b=2\n--- response_body\n3\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: fib by arg\n--- config\n    location /fib {\n        set_by_lua $res \"local function fib(n) if n > 2 then return fib(n-1)+fib(n-2) else return 1 end end return fib(tonumber(ngx.arg[1]))\" $arg_n;\n        echo $res;\n    }\n--- request\nGET /fib?n=10\n--- response_body\n55\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: adder\n--- config\n    location = /adder {\n        set_by_lua $res\n            \"local a = tonumber(ngx.arg[1])\n             local b = tonumber(ngx.arg[2])\n             return a + b\" $arg_a $arg_b;\n\n        echo $res;\n    }\n--- request\nGET /adder?a=25&b=75\n--- response_body\n100\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: read nginx variables directly from within Lua\n--- config\n    location = /set-both {\n        set $b 32;\n        set_by_lua $a \"return tonumber(ngx.var.b) + 1\";\n\n        echo \"a = $a\";\n    }\n--- request\nGET /set-both\n--- response_body\na = 33\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: set nginx variables directly from within Lua\n--- config\n    location = /set-both {\n        set $b \"\";\n        set_by_lua $a \"ngx.var.b = 32; return 7\";\n\n        echo \"a = $a\";\n        echo \"b = $b\";\n    }\n--- request\nGET /set-both\n--- response_body\na = 7\nb = 32\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: set non-existent nginx variables\n--- config\n    location = /set-both {\n        #set $b \"\";\n        set_by_lua $a \"ngx.var.b = 32; return 7\";\n\n        echo \"a = $a\";\n    }\n--- request\nGET /set-both\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nvariable \"b\" not found for writing; maybe it is a built-in variable that is not changeable or you forgot to use \"set $b '';\" in the config file to define it first\n\n\n\n=== TEST 10: set quote sql str\n--- config\n    location = /set {\n        set $a \"\";\n        set_by_lua $a \"return ngx.quote_sql_str(ngx.var.a)\";\n        echo $a;\n    }\n--- request\nGET /set\n--- response_body\n''\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: set md5\n--- config\n    location = /md5 {\n        set_by_lua $a 'return ngx.md5(\"hello\")';\n        echo $a;\n    }\n--- request\nGET /md5\n--- response_body\n5d41402abc4b2a76b9719d911017c592\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: no ngx.print\n--- config\n    location /lua {\n        set_by_lua $res \"ngx.print(32) return 1\";\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nAPI disabled in the context of set_by_lua*\n\n\n\n=== TEST 13: no ngx.say\n--- config\n    location /lua {\n        set_by_lua $res \"ngx.say(32) return 1\";\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nAPI disabled in the context of set_by_lua*\n\n\n\n=== TEST 14: no ngx.flush\n--- config\n    location /lua {\n        set_by_lua $res \"ngx.flush()\";\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nAPI disabled in the context of set_by_lua*\n\n\n\n=== TEST 15: no ngx.eof\n--- config\n    location /lua {\n        set_by_lua $res \"ngx.eof()\";\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nAPI disabled in the context of set_by_lua*\n\n\n\n=== TEST 16: no ngx.send_headers\n--- config\n    location /lua {\n        set_by_lua $res \"ngx.send_headers()\";\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nAPI disabled in the context of set_by_lua*\n\n\n\n=== TEST 17: no ngx.location.capture\n--- config\n    location /lua {\n        set_by_lua $res 'ngx.location.capture(\"/sub\")';\n        echo $res;\n    }\n\n    location /sub {\n        echo sub;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nAPI disabled in the context of set_by_lua*\n\n\n\n=== TEST 18: no ngx.location.capture_multi\n--- config\n    location /lua {\n        set_by_lua $res 'ngx.location.capture_multi{{\"/sub\"}}';\n        echo $res;\n    }\n\n    location /sub {\n        echo sub;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nAPI disabled in the context of set_by_lua*\n\n\n\n=== TEST 19: no ngx.exit\n--- config\n    location /lua {\n        set_by_lua $res 'ngx.exit(0)';\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nAPI disabled in the context of set_by_lua*\n\n\n\n=== TEST 20: no ngx.redirect\n--- config\n    location /lua {\n        set_by_lua $res 'ngx.redirect(\"/blah\")';\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nAPI disabled in the context of set_by_lua*\n\n\n\n=== TEST 21: no ngx.exec\n--- config\n    location /lua {\n        set_by_lua $res 'ngx.exec(\"/blah\")';\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nAPI disabled in the context of set_by_lua*\n\n\n\n=== TEST 22: no ngx.req.set_uri(uri, true)\n--- config\n    location /lua {\n        set_by_lua $res 'ngx.req.set_uri(\"/blah\", true)';\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nAPI disabled in the context of set_by_lua*\n\n\n\n=== TEST 23: ngx.req.set_uri(uri) exists\n--- config\n    location /lua {\n        set_by_lua $res 'ngx.req.set_uri(\"/blah\") return 1';\n        echo $uri;\n    }\n--- request\nGET /lua\n--- response_body\n/blah\n--- no_error_log\n[error]\n\n\n\n=== TEST 24: no ngx.req.read_body()\n--- config\n    location /lua {\n        set_by_lua $res 'ngx.req.read_body()';\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/(?:API disabled in the context of set_by_lua\\*|http3 requests are not supported without content-length header)/ms\n\n\n\n=== TEST 25: no ngx.req.socket()\n--- config\n    location /lua {\n        set_by_lua $res 'return ngx.req.socket()';\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nmy $err_log;\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $err_log = \"http v3 not supported yet\";\n} else {\n    $err_log = \"API disabled in the context of set_by_lua*\";\n}\n\n$err_log;\n\n\n\n=== TEST 26: no ngx.socket.tcp()\n--- config\n    location /lua {\n        set_by_lua $res 'return ngx.socket.tcp()';\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nAPI disabled in the context of set_by_lua*\n\n\n\n=== TEST 27: no ngx.socket.connect()\n--- config\n    location /lua {\n        set_by_lua $res 'return ngx.socket.connect(\"127.0.0.1\", 80)';\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nAPI disabled in the context of set_by_lua*\n\n\n\n=== TEST 28: set $limit_rate (variables with set_handler)\n--- config\n    location /lua {\n        set $limit_rate 1000;\n        rewrite_by_lua '\n            ngx.var.limit_rate = 180;\n        ';\n        echo \"limit rate = $limit_rate\";\n    }\n--- request\n    GET /lua\n--- response_body\nlimit rate = 180\n--- no_error_log\n[error]\n\n\n\n=== TEST 29: set $args and read $query_string\n--- config\n    location /lua {\n        set $args 'hello';\n        rewrite_by_lua '\n            ngx.var.args = \"world\";\n        ';\n        echo $query_string;\n    }\n--- request\n    GET /lua\n--- response_body\nworld\n--- no_error_log\n[error]\n\n\n\n=== TEST 30: set $arg_xxx\n--- config\n    location /lua {\n        rewrite_by_lua '\n            ngx.var.arg_foo = \"world\";\n        ';\n        echo $arg_foo;\n    }\n--- request\n    GET /lua?foo=3\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nvariable \"arg_foo\" not found for writing; maybe it is a built-in variable that is not changeable or you forgot to use \"set $arg_foo '';\" in the config file to define it first\n\n\n\n=== TEST 31: symbol $ in lua code of set_by_lua\n--- config\n    location /lua {\n        set_by_lua $res 'return \"$unknown\"';\n        echo $res;\n    }\n--- request\n    GET /lua\n--- response_body\n$unknown\n--- no_error_log\n[error]\n\n\n\n=== TEST 32: symbol $ in lua code of set_by_lua_file\n--- config\n    location /lua {\n        set_by_lua_file $res html/a.lua;\n        echo $res;\n    }\n--- user_files\n>>> a.lua\nreturn \"$unknown\"\n--- request\n    GET /lua\n--- response_body\n$unknown\n--- no_error_log\n[error]\n\n\n\n=== TEST 33: external script files with arguments\n--- config\n    location /lua {\n        set_by_lua_file $res html/a.lua $arg_a $arg_b;\n        echo $res;\n    }\n--- user_files\n>>> a.lua\nreturn ngx.arg[1] + ngx.arg[2]\n--- request\nGET /lua?a=5&b=2\n--- response_body\n7\n--- no_error_log\n[error]\n\n\n\n=== TEST 34: variables in set_by_lua_file's file path\n--- config\n    location /lua {\n        set $path \"html/a.lua\";\n        set_by_lua_file $res $path $arg_a $arg_b;\n        echo $res;\n    }\n--- user_files\n>>> a.lua\nreturn ngx.arg[1] + ngx.arg[2]\n--- request\nGET /lua?a=5&b=2\n--- response_body\n7\n--- no_error_log\n[error]\n\n\n\n=== TEST 35: lua error (string)\n--- config\n    location /lua {\n        set_by_lua $res 'error(\"Bad\")';\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nfailed to run set_by_lua*: set_by_lua(nginx.conf:40):1: Bad\n\n\n\n=== TEST 36: lua error (nil)\n--- config\n    location /lua {\n        set_by_lua $res 'error(nil)';\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nfailed to run set_by_lua*: unknown reason\n\n\n\n=== TEST 37: globals are shared in all requests.\n--- config\n    location /lua {\n        set_by_lua_block $res {\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.INFO, \"old foo: \", foo)\n                foo = foo + 1\n            end\n            return foo\n        }\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body_like chomp\n\\A[12]\n\\z\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/(old foo: \\d+|writing a global Lua variable \\('\\w+'\\))/\n--- grep_error_log_out eval\n[\"writing a global Lua variable \\('foo'\\)\\n\", \"old foo: 1\\n\"]\n\n\n\n=== TEST 38: user modules using ngx.arg\n--- http_config\n    lua_package_path \"$prefix/html/?.lua;;\";\n--- config\n    location /lua {\n        set_by_lua $res 'local foo = require \"foo\" return foo.go()' $arg_a $arg_b;\n        echo $res;\n    }\n--- user_files\n>>> foo.lua\nmodule(\"foo\", package.seeall)\n\nfunction go()\n    return ngx.arg[1] + ngx.arg[2]\nend\n--- request\nGET /lua?a=1&b=2\n--- response_body\n3\n--- no_error_log\n[error]\n\n\n\n=== TEST 39: server scope (inline)\n--- config\n    location /lua {\n        set $a \"[$res]\";\n        echo $a;\n    }\n    set_by_lua $res \"return 1+1\";\n--- request\nGET /lua\n--- response_body\n[2]\n--- no_error_log\n[error]\n\n\n\n=== TEST 40: server if scope (inline)\n--- config\n    location /lua {\n        set $a \"[$res]\";\n        echo $a;\n    }\n    if ($arg_name = \"jim\") {\n        set_by_lua $res \"return 1+1\";\n    }\n--- request\nGET /lua?name=jim\n--- response_body\n[2]\n--- no_error_log\n[error]\n\n\n\n=== TEST 41: location if scope (inline)\n--- config\n    location /lua {\n        if ($arg_name = \"jim\") {\n            set_by_lua $res \"return 1+1\";\n            set $a \"[$res]\";\n            echo $a;\n        }\n    }\n--- request\nGET /lua?name=jim\n--- response_body\n[2]\n--- no_error_log\n[error]\n\n\n\n=== TEST 42: server scope (file)\n--- config\n    location /lua {\n        set $a \"[$res]\";\n        echo $a;\n    }\n    set_by_lua_file $res html/a.lua;\n--- user_files\n>>> a.lua\nreturn 1+1\n--- request\nGET /lua\n--- response_body\n[2]\n--- no_error_log\n[error]\n\n\n\n=== TEST 43: server if scope (file)\n--- config\n    location /lua {\n        set $a \"[$res]\";\n        echo $a;\n    }\n    if ($arg_name = \"jim\") {\n        set_by_lua_file $res html/a.lua;\n    }\n--- request\nGET /lua?name=jim\n--- user_files\n>>> a.lua\nreturn 1+1\n--- response_body\n[2]\n--- no_error_log\n[error]\n\n\n\n=== TEST 44: location if scope (file)\n--- config\n    location /lua {\n        if ($arg_name = \"jim\") {\n            set_by_lua_file $res html/a.lua;\n            set $a \"[$res]\";\n            echo $a;\n        }\n    }\n--- user_files\n>>> a.lua\nreturn 1+1\n--- request\nGET /lua?name=jim\n--- response_body\n[2]\n--- no_error_log\n[error]\n\n\n\n=== TEST 45: backtrace\n--- config\n    location /t {\n        set_by_lua $a '\n            local bar\n            local foo\n            function foo()\n                bar()\n            end\n\n            function bar()\n                error(\"something bad happened\")\n            end\n\n            foo()\n        ';\n        echo ok;\n    }\n--- request\n    GET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nsomething bad happened\nstack traceback:\nin function 'error'\nin function 'bar'\nin function 'foo'\n\n\n\n=== TEST 46: Lua file does not exist\n--- config\n    location /lua {\n        set_by_lua_file $a html/test2.lua;\n    }\n--- user_files\n>>> test.lua\nv = ngx.var[\"request_uri\"]\nngx.print(\"request_uri: \", v, \"\\n\")\n--- request\nGET /lua?a=1&b=2\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/failed to load external Lua file \".*?test2\\.lua\": cannot open .*? No such file or directory/\n"
  },
  {
    "path": "t/002-content.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 2 + 32);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: basic print\n--- config\n    location /lua {\n        # NOTE: the newline escape sequence must be double-escaped, as nginx config\n        # parser will unescape first!\n        content_by_lua '\n            local ok, err = ngx.print(\"Hello, Lua!\\\\n\")\n            if not ok then\n                ngx.log(ngx.ERR, \"print failed: \", err)\n            end\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nHello, Lua!\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/lua caching unused lua thread|lua reusing cached lua thread/\n--- grep_error_log_out eval\n[\n    \"lua caching unused lua thread\\n\",\n    \"lua reusing cached lua thread\nlua caching unused lua thread\n\",\n]\n\n\n\n=== TEST 2: basic say\n--- config\n    location /say {\n        # NOTE: the newline escape sequence must be double-escaped, as nginx config\n        # parser will unescape first!\n        content_by_lua '\n            local ok, err = ngx.say(\"Hello, Lua!\")\n            if not ok then\n                ngx.log(ngx.ERR, \"say failed: \", err)\n                return\n            end\n            local ok, err = ngx.say(\"Yay! \", 123)\n            if not ok then\n                ngx.log(ngx.ERR, \"say failed: \", err)\n                return\n            end\n        ';\n    }\n--- request\nGET /say\n--- response_body\nHello, Lua!\nYay! 123\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: no ngx.echo\n--- config\n    location /lua {\n        content_by_lua 'ngx.echo(\"Hello, Lua!\\\\n\")';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/content_by_lua\\(nginx\\.conf:\\d+\\):1: attempt to call field 'echo' \\(a nil value\\)/\n\n\n\n=== TEST 4: variable\n--- config\n    location /lua {\n        # NOTE: the newline escape sequence must be double-escaped, as nginx config\n        # parser will unescape first!\n        content_by_lua 'local v = ngx.var[\"request_uri\"] ngx.print(\"request_uri: \", v, \"\\\\n\")';\n    }\n--- request\nGET /lua?a=1&b=2\n--- response_body\nrequest_uri: /lua?a=1&b=2\n\n\n\n=== TEST 5: variable (file)\n--- config\n    location /lua {\n        content_by_lua_file html/test.lua;\n    }\n--- user_files\n>>> test.lua\nlocal v = ngx.var[\"request_uri\"]\nngx.print(\"request_uri: \", v, \"\\n\")\n--- request\nGET /lua?a=1&b=2\n--- response_body\nrequest_uri: /lua?a=1&b=2\n\n\n\n=== TEST 6: calc expression\n--- config\n    location /lua {\n        content_by_lua_file html/calc.lua;\n    }\n--- user_files\n>>> calc.lua\nlocal function uri_unescape(uri)\n    local function convert(hex)\n        return string.char(tonumber(\"0x\"..hex))\n    end\n    local s = string.gsub(uri, \"%%([0-9a-fA-F][0-9a-fA-F])\", convert)\n    return s\nend\n\nlocal function eval_exp(str)\n    return loadstring(\"return \"..str)()\nend\n\nlocal exp_str = ngx.var[\"arg_exp\"]\n-- print(\"exp: '\", exp_str, \"'\\n\")\nlocal status, res\nstatus, res = pcall(uri_unescape, exp_str)\nif not status then\n    ngx.print(\"error: \", res, \"\\n\")\n    return\nend\nstatus, res = pcall(eval_exp, res)\nif status then\n    ngx.print(\"result: \", res, \"\\n\")\nelse\n    ngx.print(\"error: \", res, \"\\n\")\nend\n--- request\nGET /lua?exp=1%2B2*math.sin(3)%2Fmath.exp(4)-math.sqrt(2)\n--- response_body\nresult: -0.4090441561579\n\n\n\n=== TEST 7: read $arg_xxx\n--- config\n    location = /lua {\n        content_by_lua 'local who = ngx.var.arg_who\n            ngx.print(\"Hello, \", who, \"!\")';\n    }\n--- request\nGET /lua?who=agentzh\n--- response_body chomp\nHello, agentzh!\n\n\n\n=== TEST 8: capture location\n--- config\n    location /other {\n        echo \"hello, world\";\n    }\n\n    location /lua {\n        content_by_lua 'local res = ngx.location.capture(\"/other\"); ngx.print(\"status=\", res.status, \" \"); ngx.print(\"body=\", res.body)';\n    }\n--- request\nGET /lua\n--- response_body\nstatus=200 body=hello, world\n\n\n\nei= TEST 9: capture non-existed location\n--- config\n    location /lua {\n        content_by_lua 'local res = ngx.location.capture(\"/other\"); ngx.print(\"status=\", res.status)';\n    }\n--- request\nGET /lua\n--- response_body: status=404\n\n\n\n=== TEST 9: invalid capture location (not as expected...)\n--- config\n    location /lua {\n        content_by_lua 'local res = ngx.location.capture(\"*(#*\"); ngx.say(\"res=\", res.status)';\n    }\n--- request\nGET /lua\n--- response_body\nres=404\n\n\n\n=== TEST 10: nil is \"nil\"\n--- config\n    location /lua {\n        content_by_lua 'ngx.say(nil)';\n    }\n--- request\nGET /lua\n--- response_body\nnil\n\n\n\n=== TEST 11: write boolean\n--- config\n    location /lua {\n        content_by_lua 'ngx.say(true, \" \", false)';\n    }\n--- request\nGET /lua\n--- response_body\ntrue false\n\n\n\n=== TEST 12: bad argument type to ngx.location.capture\n--- config\n    location /lua {\n        content_by_lua 'ngx.location.capture(nil)';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 13: capture location (default 0);\n--- config\n location /recur {\n       content_by_lua '\n           local num = tonumber(ngx.var.arg_num) or 0;\n           ngx.print(\"num is: \", num, \"\\\\n\");\n\n           if (num > 0) then\n               local res = ngx.location.capture(\"/recur?num=\"..tostring(num - 1));\n               ngx.print(\"status=\", res.status, \" \");\n               ngx.print(\"body=\", res.body, \"\\\\n\");\n           else\n               ngx.print(\"end\\\\n\");\n           end\n           ';\n   }\n--- request\nGET /recur\n--- response_body\nnum is: 0\nend\n\n\n\n=== TEST 14: capture location\n--- config\n location /recur {\n       content_by_lua '\n           local num = tonumber(ngx.var.arg_num) or 0;\n           ngx.print(\"num is: \", num, \"\\\\n\");\n\n           if (num > 0) then\n               local res = ngx.location.capture(\"/recur?num=\"..tostring(num - 1));\n               ngx.print(\"status=\", res.status, \" \");\n               ngx.print(\"body=\", res.body);\n           else\n               ngx.print(\"end\\\\n\");\n           end\n           ';\n   }\n--- request\nGET /recur?num=3\n--- response_body\nnum is: 3\nstatus=200 body=num is: 2\nstatus=200 body=num is: 1\nstatus=200 body=num is: 0\nend\n\n\n\n=== TEST 15: setting nginx variables from within Lua\n--- config\n location /set {\n       set $a \"\";\n       content_by_lua 'ngx.var.a = 32; ngx.say(ngx.var.a)';\n       add_header Foo $a;\n   }\n--- request\nGET /set\n--- response_headers\nFoo: 32\n--- response_body\n32\n\n\n\n=== TEST 16: nginx quote sql string 1\n--- config\n location /set {\n       set $a 'hello\\n\\r\\'\"\\\\';\n       content_by_lua 'ngx.say(ngx.quote_sql_str(ngx.var.a))';\n   }\n--- request\nGET /set\n--- response_body\n'hello\\n\\r\\'\\\"\\\\'\n\n\n\n=== TEST 17: nginx quote sql string 2\n--- config\nlocation /set {\n    set $a \"hello\\n\\r'\\\"\\\\\";\n    content_by_lua 'ngx.say(ngx.quote_sql_str(ngx.var.a))';\n}\n--- request\nGET /set\n--- response_body\n'hello\\n\\r\\'\\\"\\\\'\n\n\n\n=== TEST 18: use dollar\n--- config\nlocation /set {\n    content_by_lua '\n        local s = \"hello 112\";\n        ngx.say(string.find(s, \"%d+$\"))';\n}\n--- request\nGET /set\n--- response_body\n79\n\n\n\n=== TEST 19: subrequests do not share variables of main requests by default\n--- config\nlocation /sub {\n    echo $a;\n}\nlocation /parent {\n    set $a 12;\n    content_by_lua 'local res = ngx.location.capture(\"/sub\"); ngx.print(res.body)';\n}\n--- request\nGET /parent\n--- response_body eval: \"\\n\"\n\n\n\n=== TEST 20: subrequests can share variables of main requests\n--- config\nlocation /sub {\n    echo $a;\n}\nlocation /parent {\n    set $a 12;\n    content_by_lua '\n        local res = ngx.location.capture(\n            \"/sub\",\n            { share_all_vars = true }\n        );\n        ngx.print(res.body)\n    ';\n}\n--- request\nGET /parent\n--- response_body\n12\n\n\n\n=== TEST 21: main requests use subrequests' variables\n--- config\nlocation /sub {\n    set $a 12;\n}\nlocation /parent {\n    content_by_lua '\n        local res = ngx.location.capture(\"/sub\", { share_all_vars = true });\n        ngx.say(ngx.var.a)\n    ';\n}\n--- request\nGET /parent\n--- response_body\n12\n\n\n\n=== TEST 22: main requests do NOT use subrequests' variables\n--- config\nlocation /sub {\n    set $a 12;\n}\nlocation /parent {\n    content_by_lua '\n        local res = ngx.location.capture(\"/sub\", { share_all_vars = false });\n        ngx.say(ngx.var.a)\n    ';\n}\n--- request\nGET /parent\n--- response_body_like eval: \"\\n\"\n\n\n\n=== TEST 23: capture location headers\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo \"hello, world\";\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\");\n            ngx.say(\"type: \", res.header[\"Content-Type\"]);\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ntype: foo/bar\n\n\n\n=== TEST 24: capture location multi-value headers\n--- config\n    location /other {\n        #echo \"hello, world\";\n        content_by_lua '\n            ngx.header[\"Set-Cookie\"] = {\"a\", \"hello, world\", \"foo\"}\n            local ok, err = ngx.eof()\n            if not ok then\n                ngx.log(ngx.ERR, \"eof failed: \", err)\n                return\n            end\n        ';\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\");\n            ngx.say(\"type: \", type(res.header[\"Set-Cookie\"]));\n            ngx.say(\"len: \", #res.header[\"Set-Cookie\"]);\n            ngx.say(\"value: \", table.concat(res.header[\"Set-Cookie\"], \"|\"))\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ntype: table\nlen: 3\nvalue: a|hello, world|foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 25: capture location headers\n--- config\n    location /other {\n        default_type 'foo/bar';\n        content_by_lua '\n            ngx.header.Bar = \"Bah\";\n        ';\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\");\n            ngx.say(\"type: \", res.header[\"Content-Type\"]);\n            ngx.say(\"Bar: \", res.header[\"Bar\"]);\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ntype: foo/bar\nBar: Bah\n\n\n\n=== TEST 26: capture location headers\n--- config\n    location /other {\n        default_type 'foo/bar';\n        content_by_lua '\n            ngx.header.Bar = \"Bah\";\n            ngx.header.Bar = nil;\n        ';\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\");\n            ngx.say(\"type: \", res.header[\"Content-Type\"]);\n            ngx.say(\"Bar: \", res.header[\"Bar\"] or \"nil\");\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ntype: foo/bar\nBar: nil\n\n\n\n=== TEST 27: HTTP 1.0 response\n--- config\n    location /lua {\n        content_by_lua '\n            local data = \"hello, world\"\n            -- ngx.header[\"Content-Length\"] = #data\n            -- ngx.header.content_length = #data\n            ngx.print(data)\n        ';\n    }\n    location /main {\n        proxy_pass http://127.0.0.1:$server_port/lua;\n    }\n--- request\nGET /main\n--- response_headers\nContent-Length: 12\n--- response_body chop\nhello, world\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 28: multiple eof\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(\"Hi\")\n\n            local ok, err = ngx.eof()\n            if not ok then\n                ngx.log(ngx.WARN, \"eof failed: \", err)\n                return\n            end\n\n            ok, err = ngx.eof()\n            if not ok then\n                ngx.log(ngx.WARN, \"eof failed: \", err)\n                return\n            end\n\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nHi\n--- no_error_log\n[error]\n--- error_log\neof failed: seen eof\n\n\n\n=== TEST 29: nginx vars in script path\n--- config\n    location ~ ^/lua/(.+)$ {\n        content_by_lua_file html/$1.lua;\n    }\n--- user_files\n>>> calc.lua\nlocal a,b = ngx.var.arg_a, ngx.var.arg_b\nngx.say(a+b)\n--- request\nGET /lua/calc?a=19&b=81\n--- response_body\n100\n\n\n\n=== TEST 30: nginx vars in script path\n--- config\n    location ~ ^/lua/(.+)$ {\n        content_by_lua_file html/$1.lua;\n    }\n    location /main {\n        echo_location /lua/sum a=3&b=2;\n        echo_location /lua/diff a=3&b=2;\n    }\n--- user_files\n>>> sum.lua\nlocal a,b = ngx.var.arg_a, ngx.var.arg_b\nngx.say(a+b)\n>>> diff.lua\nlocal a,b = ngx.var.arg_a, ngx.var.arg_b\nngx.say(a-b)\n--- request\nGET /main\n--- response_body\n5\n1\n\n\n\n=== TEST 31: basic print (HEAD + HTTP 1.1)\n--- config\n    location /lua {\n        # NOTE: the newline escape sequence must be double-escaped, as nginx config\n        # parser will unescape first!\n        content_by_lua 'ngx.print(\"Hello, Lua!\\\\n\")';\n    }\n--- request\nHEAD /lua\n--- response_body\n\n\n\n=== TEST 32: basic print (HEAD + HTTP 1.0)\n--- config\n    location /lua {\n        # NOTE: the newline escape sequence must be double-escaped, as nginx config\n        # parser will unescape first!\n        content_by_lua '\n            ngx.print(\"Hello, Lua!\\\\n\")\n        ';\n    }\n--- request\nHEAD /lua HTTP/1.0\n--- response_headers\n!Content-Length\n--- response_body\n\n\n\n=== TEST 33: headers_sent & HEAD\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(ngx.headers_sent)\n            local ok, err = ngx.flush()\n            if not ok then\n                ngx.log(ngx.WARN, \"failed to flush: \", err)\n                return\n            end\n            ngx.say(ngx.headers_sent)\n        ';\n    }\n--- request\nHEAD /lua\n--- response_body\n--- no_error_log\n[error]\n--- error_log\nfailed to flush: header only\n\n\n\n=== TEST 34: HEAD & ngx.say\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.send_headers()\n            local ok, err = ngx.say(ngx.headers_sent)\n            if not ok then\n                ngx.log(ngx.WARN, \"failed to say: \", err)\n                return\n            end\n        ';\n    }\n--- request\nHEAD /lua\n--- response_body\n--- no_error_log\n[error]\n--- error_log\nfailed to say: header only\n\n\n\n=== TEST 35: ngx.eof before ngx.say\n--- config\n    location /lua {\n        content_by_lua '\n            local ok, err = ngx.eof()\n            if not ok then\n                ngx.log(ngx.ERR, \"eof failed: \", err)\n                return\n            end\n\n            ok, err = ngx.say(ngx.headers_sent)\n            if not ok then\n                ngx.log(ngx.WARN, \"failed to say: \", err)\n                return\n            end\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n--- no_error_log\n[error]\n--- error_log\nfailed to say: seen eof\n\n\n\n=== TEST 36: headers_sent + GET\n--- config\n    location /lua {\n        content_by_lua '\n            -- print(\"headers sent: \", ngx.headers_sent)\n            ngx.say(ngx.headers_sent)\n            ngx.say(ngx.headers_sent)\n            -- ngx.flush()\n            ngx.say(ngx.headers_sent)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nfalse\ntrue\ntrue\n\n\n\n=== TEST 37: HTTP 1.0 response with Content-Length\n--- config\n    location /lua {\n        content_by_lua '\n            local data = \"hello,\\\\nworld\\\\n\"\n            ngx.header[\"Content-Length\"] = #data\n            ngx.say(\"hello,\")\n            ngx.flush()\n            -- ngx.location.capture(\"/sleep\")\n            ngx.say(\"world\")\n        ';\n    }\n    location /sleep {\n        echo_sleep 2;\n    }\n    location /main {\n        proxy_pass http://127.0.0.1:$server_port/lua;\n    }\n--- request\nGET /main\n--- response_headers\nContent-Length: 13\n--- response_body\nhello,\nworld\n--- timeout: 5\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 38: ngx.print table arguments (github issue #54)\n--- config\n    location /t {\n        content_by_lua 'ngx.print({10, {0, 5}, 15}, 32)';\n    }\n--- request\n    GET /t\n--- response_body chop\n10051532\n\n\n\n=== TEST 39: ngx.say table arguments (github issue #54)\n--- config\n    location /t {\n        content_by_lua 'ngx.say({10, {0, \"5\"}, 15}, 32)';\n    }\n--- request\n    GET /t\n--- response_body\n10051532\n\n\n\n=== TEST 40: Lua file does not exist\n--- config\n    location /lua {\n        content_by_lua_file html/test2.lua;\n    }\n--- user_files\n>>> test.lua\nlocal v = ngx.var[\"request_uri\"]\nngx.print(\"request_uri: \", v, \"\\n\")\n--- request\nGET /lua?a=1&b=2\n--- response_body_like: 404 Not Found\n--- error_code: 404\n--- error_log eval\nqr/failed to load external Lua file \".*?test2\\.lua\": cannot open .*? No such file or directory/\n\n\n\n=== TEST 41: .lua file with shebang\n--- config\n    location /lua {\n        content_by_lua_file html/test.lua;\n    }\n--- user_files\n>>> test.lua\n#!/bin/lua\n\nngx.say(\"line \", debug.getinfo(1).currentline)\n--- request\nGET /lua?a=1&b=2\n--- response_body\nline 3\n--- no_error_log\n[error]\n\n\n\n=== TEST 42: syntax error in inlined Lua code\n--- config\n    location /lua {\n        content_by_lua 'for end';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/failed to load inlined Lua code: content_by_lua\\(nginx.conf:40\\)/\n\n\n\n=== TEST 43: syntax error in content_by_lua_block\n--- config\n    location /lua {\n\n        content_by_lua_block {\n            'for end';\n        }\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/failed to load inlined Lua code: content_by_lua\\(nginx.conf:41\\)/\n\n\n\n=== TEST 44: syntax error in second content_by_lua_block\n--- config\n    location /foo {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n\n    location /lua {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/failed to load inlined Lua code: content_by_lua\\(nginx.conf:46\\)/\n\n\n\n=== TEST 45: syntax error in thrid content_by_lua_block\n--- config\n    location /foo {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n\n    location /bar {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n\n    location /lua {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/failed to load inlined Lua code: content_by_lua\\(nginx.conf:52\\)/\n\n\n\n=== TEST 46: syntax error in included file\n--- config\n    location /foo {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n\n    location /bar {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n\n    include ../html/lua.conf;\n--- user_files\n>>> lua.conf\n    location /lua {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nfailed to load inlined Lua code: content_by_lua(../html/lua.conf:2):2: unexpected symbol near ''for end''\n\n\n\n=== TEST 47: syntax error with very long filename\n--- config\n    location /foo {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n\n    location /bar {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n\n    include ../html/1234567890123456789012345678901234.conf;\n--- user_files\n>>> 1234567890123456789012345678901234.conf\n    location /lua {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nfailed to load inlined Lua code: content_by_lua(...234567890123456789012345678901234.conf:2)\n\n\n\n=== TEST 48: syntax error in /tmp/lua.conf\n--- config\n    location /foo {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n\n    location /bar {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n\n    include /tmp/lua.conf;\n--- user_files\n>>> /tmp/lua.conf\n    location /lua {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nfailed to load inlined Lua code: content_by_lua(/tmp/lua.conf:2)\n\n\n\n=== TEST 49: syntax error in /tmp/12345678901234567890123456789012345.conf\n--- config\n    location /foo {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n\n    location /bar {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n\n    include /tmp/12345678901234567890123456789012345.conf;\n\n--- user_files\n>>> /tmp/12345678901234567890123456789012345.conf\n    location /lua {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nfailed to load inlined Lua code: content_by_lua(...345678901234567890123456789012345.conf:2)\n\n\n\n=== TEST 50: the error line number greater than 9\n--- config\n    location /foo {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n\n    location /bar {\n        content_by_lua_block {\n            'for end';\n        }\n    }\n\n    include /tmp/12345678901234567890123456789012345.conf;\n\n--- user_files\n>>> /tmp/12345678901234567890123456789012345.conf\n    location /lua {\n\n\n\n\n\n\n\n\n\n\n\n\n        content_by_lua_block {\n            'for end';\n        }\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nfailed to load inlined Lua code: content_by_lua(...45678901234567890123456789012345.conf:14)\n\n\n\n=== TEST 51: Lua file permission denied\n--- config\n    location /lua {\n        content_by_lua_file /etc/shadow;\n    }\n--- request\nGET /lua\n--- response_body_like: 503 Service Temporarily Unavailable\n--- error_code: 503\n\n\n\n=== TEST 52: send_header trigger filter finalize does not clear the ctx\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.header[\"Last-Modified\"] = ngx.http_time(ngx.time())\n            ngx.send_headers()\n            local phase = ngx.get_phase()\n        }\n        header_filter_by_lua_block {\n            ngx.header[\"X-Hello-World\"] = \"Hello World\"\n        }\n    }\n--- request\nGET /lua\n--- more_headers\nIf-Unmodified-Since: Wed, 01 Jan 2020 07:28:00 GMT\n--- error_code: 412\n--- no_error_log\nunknown phase: 0\n--- skip_eval: 2:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n"
  },
  {
    "path": "t/003-errors.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(1);\n\nplan tests => blocks() * repeat_each() * 2;\n\n#$ENV{LUA_PATH} = $ENV{HOME} . '/work/JSON4Lua-0.9.30/json/?.lua';\n\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: syntax error in lua code chunk\n--- config\n    location /lua {\n        set_by_lua $res \"local a\n            a = a+;\n            return a\";\n        echo $res;\n    }\n--- request\nGET /lua\n--- error_code: 500\n--- response_body_like: 500 Internal Server Error\n\n\n\n=== TEST 2: syntax error in lua file\n--- config\n    location /lua {\n        set_by_lua_file $res 'html/test.lua';\n        echo $res;\n    }\n--- user_files\n>>> test.lua\nlocal a\na = 3 +;\nreturn a\n--- request\nGET /lua\n--- error_code: 500\n--- response_body_like: 500 Internal Server Error\n\n\n\n=== TEST 3: syntax error in lua file (from Guang Feng)\n--- config\n    location /lua {\n        set $res '[{\"a\":32},{\"b\":64}]';\n        #set $res '[{\"friend_userid\":1750146},{\"friend_userid\":1750150},{\"friend_userid\":1750153},{\"friend_userid\":1750166},{\"friend_userid\":1750181},{\"friend_userid\":1750186},{\"friend_userid\":1750195},{\"friend_userid\":1750232}]';\n        set_by_lua_file $list 'html/test.lua' $res;\n        #set_by_lua_file $list 'html/feed.lua' $res;\n        echo $list;\n    }\n--- user_files\n>>> test.lua\n-- local j = require('json')\nlocal p = ngx.arg[1]\nreturn p\n>>> feed.lua\nlocal s = require(\"json\")\nlocal function explode(d,p)\n   local t, ll\n   t={}\n   ll=0\n   if(#p == 1) then return p end\n       while true do\n       l=string.find(p,d,ll+1,true) \n           if l~=nil then \n         table.insert(t, string.sub(p,ll,l-1)) \n         ll=l+1 \n           else\n         table.insert(t, string.sub(p,ll)) \n         break \n         end\n     end\nreturn t\n end\n\nlocal a = explode(',', string.sub(ngx.arg[1], 2, -1))\nlocal x = {}\nfor i,v in ipairs(a) do table.insert(x,s.decode(v).friend_userid) end\nreturn table.concat(x,',')\n--- request\nGET /lua\n--- response_body\n[{\"a\":32},{\"b\":64}]\n\n\n\n=== TEST 4: 500 in subrequest\n--- config\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/err\")\n            ngx.say(res.status);\n        ';\n    }\n    location /err {\n        return 500;\n    }\n--- request\nGET /main\n--- response_body\n500\n\n\n\n=== TEST 5: drizzle_pass 500 in subrequest\n--- config\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/err\")\n            ngx.say(res.status);\n        ';\n    }\n    location /err {\n        set $back 'blah-blah';\n        drizzle_pass $back;\n    }\n--- request\nGET /main\n--- response_body\n500\n"
  },
  {
    "path": "t/004-require.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#log_level('warn');\n\n#master_on();\n#repeat_each(120);\nrepeat_each(2);\n\nplan tests => blocks() * repeat_each() * 2;\n\nour $HtmlDir = html_dir;\n#warn $html_dir;\n\n#$ENV{LUA_PATH} = \"$html_dir/?.lua\";\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /main {\n        echo_location /load;\n        echo_location /check;\n        echo_location /check;\n    }\n\n    location /load {\n        content_by_lua '\n            package.loaded.foo = nil;\n            collectgarbage()\n            local foo = require \"foo\";\n            foo.hi()\n        ';\n    }\n\n    location /check {\n        content_by_lua '\n            local foo = package.loaded.foo\n            if foo then\n                ngx.say(\"found\")\n                foo.hi()\n            else\n                ngx.say(\"not found\")\n            end\n        ';\n    }\n--- request\nGET /main\n--- user_files\n>>> foo.lua\nmodule(..., package.seeall);\n\nngx.say(\"loading\");\n\nfunction hi ()\n    ngx.say(\"hello, foo\")\nend;\n--- response_body\nloading\nhello, foo\nfound\nhello, foo\nfound\nhello, foo\n\n\n\n=== TEST 2: sanity\n--- http_config eval\n    \"lua_package_cpath '$::HtmlDir/?.so';\"\n--- config\n    location /main {\n        content_by_lua '\n            ngx.print(package.cpath);\n        ';\n    }\n--- request\nGET /main\n--- user_files\n--- response_body_like: ^[^;]+/servroot(_\\d+)?/html/\\?\\.so$\n\n\n\n=== TEST 3: expand default path (after)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;;';\"\n--- config\n    location /main {\n        content_by_lua '\n            ngx.print(package.path);\n        ';\n    }\n--- request\nGET /main\n--- response_body_like: ^[^;]+/servroot(_\\d+)?/html/\\?\\.lua;(.+\\.lua)?;*$\n\n\n\n=== TEST 4: expand default cpath (after)\n--- http_config eval\n    \"lua_package_cpath '$::HtmlDir/?.so;;';\"\n--- config\n    location /main {\n        content_by_lua '\n            ngx.print(package.cpath);\n        ';\n    }\n--- request\nGET /main\n--- response_body_like: ^[^;]+/servroot(_\\d+)?/html/\\?\\.so;(.+\\.so)?;*$\n\n\n\n=== TEST 5: expand default path (before)\n--- http_config eval\n    \"lua_package_path ';;$::HtmlDir/?.lua';\"\n--- config\n    location /main {\n        content_by_lua '\n            ngx.print(package.path);\n        ';\n    }\n--- request\nGET /main\n--- response_body_like: ^(.+\\.lua)?;*?[^;]+/servroot(_\\d+)?/html/\\?\\.lua$\n\n\n\n=== TEST 6: expand default cpath (before)\n--- http_config eval\n    \"lua_package_cpath ';;$::HtmlDir/?.so';\"\n--- config\n    location /main {\n        content_by_lua '\n            ngx.print(package.cpath);\n        ';\n    }\n--- request\nGET /main\n--- response_body_like: ^(.+\\.so)?;*?[^;]+/servroot(_\\d+)?/html/\\?\\.so$\n\n\n\n=== TEST 7: require \"ngx\" (content_by_lua)\n--- config\n    location /ngx {\n        content_by_lua '\n            local ngx = require \"ngx\"\n            ngx.say(\"hello, world\")\n        ';\n    }\n--- request\nGET /ngx\n--- response_body\nhello, world\n\n\n\n=== TEST 8: require \"ngx\" (set_by_lua)\n--- config\n    location /ngx {\n        set_by_lua $res '\n            local ngx = require \"ngx\"\n            return ngx.escape_uri(\" \")\n        ';\n        echo $res;\n    }\n--- request\nGET /ngx\n--- response_body\n%20\n\n\n\n=== TEST 9: require \"ndk\" (content_by_lua)\n--- config\n    location /ndk {\n        content_by_lua '\n            local ndk = require \"ndk\"\n            local res = ndk.set_var.set_escape_uri(\" \")\n            ngx.say(res)\n        ';\n    }\n--- request\nGET /ndk\n--- response_body\n%20\n\n\n\n=== TEST 10: require \"ndk\" (set_by_lua)\n--- config\n    location /ndk {\n        set_by_lua $res '\n            local ndk = require \"ndk\"\n            return ndk.set_var.set_escape_uri(\" \")\n        ';\n        echo $res;\n    }\n--- request\nGET /ndk\n--- response_body\n%20\n"
  },
  {
    "path": "t/005-exit.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#repeat_each(20000);\n#repeat_each(200);\nrepeat_each(2);\n#master_on();\n#workers(1);\n#log_level('debug');\n#log_level('warn');\n#worker_connections(1024);\n\nplan tests => repeat_each() * (blocks() * 3 + 2);\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n\nour $LuaCpath = $ENV{LUA_CPATH} ||\n    '/usr/local/openresty-debug/lualib/?.so;/usr/local/openresty/lualib/?.so;;';\n\n#$ENV{LUA_PATH} = $ENV{HOME} . '/work/JSON4Lua-0.9.30/json/?.lua';\n\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: throw 403\n--- config\n    location /lua {\n        content_by_lua \"ngx.exit(403);ngx.say('hi')\";\n    }\n--- request\nGET /lua\n--- error_code: 403\n--- response_body_like: 403 Forbidden\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: throw 404\n--- config\n    location /lua {\n        content_by_lua \"ngx.exit(404);ngx.say('hi');\";\n    }\n--- request\nGET /lua\n--- error_code: 404\n--- response_body_like: 404 Not Found\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: throw 404 after sending the header and partial body\n--- config\n    location /lua {\n        content_by_lua \"ngx.say('hi');ngx.exit(404);ngx.say(', you')\";\n    }\n--- request\nGET /lua\n--- error_log\nattempt to set status 404 via ngx.exit after sending out the response status 200\n--- no_error_log\n[alert]\n--- response_body\nhi\n\n\n\n=== TEST 4: working with ngx_auth_request (succeeded)\n--- config\n    location /auth {\n        content_by_lua \"\n            if ngx.var.user == 'agentzh' then\n                ngx.eof();\n            else\n                ngx.exit(403)\n            end\";\n    }\n    location /api {\n        set $user $arg_user;\n        auth_request /auth;\n\n        echo \"Logged in\";\n    }\n--- request\nGET /api?user=agentzh\n--- error_code: 200\n--- response_body\nLogged in\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: working with ngx_auth_request (failed)\n--- config\n    location /auth {\n        content_by_lua \"\n            if ngx.var.user == 'agentzh' then\n                ngx.eof();\n            else\n                ngx.exit(403)\n            end\";\n    }\n    location /api {\n        set $user $arg_user;\n        auth_request /auth;\n\n        echo \"Logged in\";\n    }\n--- request\nGET /api?user=agentz\n--- error_code: 403\n--- response_body_like: 403 Forbidden\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: working with ngx_auth_request (simplest form, w/o ngx_memc)\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n--- no_http2\n--- http_config eval\n\"\n    lua_package_cpath '$::LuaCpath';\n    upstream backend {\n        drizzle_server 127.0.0.1:\\$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n\"\n--- config\n    location /memc {\n        internal;\n\n        set $memc_key $arg_key;\n        set $memc_exptime $arg_exptime;\n\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /conv-uid-mysql {\n        internal;\n\n        set $key \"conv-uid-$arg_uid\";\n\n        #srcache_fetch GET /memc key=$key;\n        #srcache_store PUT /memc key=$key;\n\n        default_type 'application/json';\n\n        drizzle_query \"select new_uid as uid from conv_uid where old_uid=$arg_uid\";\n        drizzle_pass backend;\n\n        rds_json on;\n    }\n\n    location /conv-uid {\n        internal;\n        content_by_lua_file 'html/foo.lua';\n    }\n    location /api {\n        set $uid $arg_uid;\n        auth_request /conv-uid;\n\n        echo \"Logged in $uid\";\n    }\n--- user_files\n>>> foo.lua\nlocal cjson = require('cjson');\nlocal old_uid = ngx.var.uid\n-- print('about to run sr')\nlocal res = ngx.location.capture('/conv-uid-mysql?uid=' .. old_uid)\nif (res.status ~= ngx.HTTP_OK) then\n    ngx.exit(res.status)\nend\n-- print('just have run sr: ' .. res.body)\nres = cjson.decode(res.body)\nif (not res or not res[1] or not res[1].uid or\n        not string.match(res[1].uid, '^%d+$')) then\n    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\nend\nngx.var.uid = res[1].uid;\n-- print('done')\n--- request\nGET /api?uid=32\n--- response_body\nLogged in 56\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: working with ngx_auth_request (simplest form)\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n--- no_http2\n--- http_config eval\n\"\n    lua_package_cpath '$::LuaCpath';\n    upstream backend {\n        drizzle_server 127.0.0.1:\\$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n\"\n--- config\n    location /memc {\n        internal;\n\n        set $memc_key $arg_key;\n        set $memc_exptime $arg_exptime;\n\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /conv-uid-mysql {\n        internal;\n\n        set $key \"conv-uid-$arg_uid\";\n\n        #srcache_fetch GET /memc key=$key;\n        #srcache_store PUT /memc key=$key;\n\n        default_type 'application/json';\n\n        drizzle_query \"select new_uid as uid from conv_uid where old_uid=$arg_uid\";\n        drizzle_pass backend;\n\n        rds_json on;\n    }\n\n    location /conv-uid {\n        internal;\n        content_by_lua_file 'html/foo.lua';\n    }\n    location /api {\n        set $uid $arg_uid;\n        auth_request /conv-uid;\n\n        echo \"Logged in $uid\";\n    }\n--- user_files\n>>> foo.lua\nlocal cjson = require('cjson');\nlocal old_uid = ngx.var.uid\n-- print('about to run sr')\nlocal res = ngx.location.capture('/conv-uid-mysql?uid=' .. old_uid)\n-- print('just have run sr' .. res.body)\nif (res.status ~= ngx.HTTP_OK) then\n    ngx.exit(res.status)\nend\nres = cjson.decode(res.body)\nif (not res or not res[1] or not res[1].uid or\n        not string.match(res[1].uid, '^%d+$')) then\n    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\nend\nngx.var.uid = res[1].uid;\n-- print('done')\n--- request\nGET /api?uid=32\n--- response_body\nLogged in 56\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: working with ngx_auth_request\n--- no_http2\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n--- http_config eval\n\"\n    lua_package_cpath '$::LuaCpath';\n    upstream backend {\n        drizzle_server 127.0.0.1:\\$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n\n    upstream memc_a {\n        server 127.0.0.1:\\$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    upstream memc_b {\n        server 127.0.0.1:\\$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    upstream_list memc_cluster memc_a memc_b;\n\"\n--- config\n    location /memc {\n        internal;\n\n        set $memc_key $arg_key;\n        set $memc_exptime $arg_exptime;\n\n        set_hashed_upstream $backend memc_cluster $arg_key;\n        memc_pass $backend;\n    }\n\n    location /conv-uid-mysql {\n        internal;\n\n        set $key \"conv-uid-$arg_uid\";\n\n        #srcache_fetch GET /memc key=$key;\n        #srcache_store PUT /memc key=$key;\n\n        default_type 'application/json';\n\n        drizzle_query \"select new_uid as uid from conv_uid where old_uid=$arg_uid\";\n        drizzle_pass backend;\n\n        rds_json on;\n    }\n\n    location /conv-uid {\n        internal;\n        content_by_lua_file 'html/foo.lua';\n    }\n    location /api {\n        set $uid $arg_uid;\n        auth_request /conv-uid;\n\n        echo \"Logged in $uid\";\n    }\n--- user_files\n>>> foo.lua\nlocal cjson = require('cjson');\nlocal old_uid = ngx.var.uid\n-- print('about to run sr')\nlocal res = ngx.location.capture('/conv-uid-mysql?uid=' .. old_uid)\n-- print('just have run sr' .. res.body)\nif (res.status ~= ngx.HTTP_OK) then\n    ngx.exit(res.status)\nend\nres = cjson.decode(res.body)\nif (not res or not res[1] or not res[1].uid or\n        not string.match(res[1].uid, '^%d+$')) then\n    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\nend\nngx.var.uid = res[1].uid;\n-- print('done')\n--- request\nGET /api?uid=32\n--- response_body\nLogged in 56\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 9: working with ngx_auth_request\n--- http_config\n    upstream backend {\n        drizzle_server 127.0.0.1:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n\n    upstream memc_a {\n        server 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n        keepalive 300;\n    }\n\n    #upstream_list memc_cluster memc_a memc_b;\n\n--- config\n    location /memc {\n        internal;\n\n        set $memc_key $arg_key;\n        set $memc_exptime $arg_exptime;\n\n        #set_hashed_upstream $backend memc_cluster $arg_key;\n        memc_pass memc_a;\n    }\n\n    location /conv-mysql {\n        internal;\n\n        set $key \"conv-uri-$query_string\";\n\n        #srcache_fetch GET /memc key=$key;\n        #srcache_store PUT /memc key=$key;\n\n        default_type 'application/json';\n\n        set_quote_sql_str $seo_uri $query_string;\n        drizzle_query \"select url from my_url_map where seo_url=$seo_uri\";\n        drizzle_pass backend;\n\n        rds_json on;\n    }\n\n    location /conv-uid {\n        internal;\n        content_by_lua_file 'html/foo.lua';\n    }\n\n    location /baz {\n        set $my_uri $uri;\n        auth_request /conv-uid;\n\n        echo_exec /jump $my_uri;\n    }\n\n    location /jump {\n        internal;\n        rewrite ^ $query_string? redirect;\n    }\n--- user_files\n>>> foo.lua\nlocal cjson = require('cjson');\nlocal seo_uri = ngx.var.my_uri\n-- print('about to run sr')\nlocal res = ngx.location.capture('/conv-mysql?' .. seo_uri)\nif (res.status ~= ngx.HTTP_OK) then\n    ngx.exit(res.status)\nend\nres = cjson.decode(res.body)\nif (not res or not res[1] or not res[1].url) then\n    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\nend\nngx.var.my_uri = res[1].url;\n-- print('done')\n--- request\nGET /baz\n--- response_body_like: 302\n--- error_code: 302\n--- response_headers\nLocation: http://localhost:$ServerPort/foo/bar\n--- SKIP\n\n\n\n=== TEST 10: throw 0\n--- config\n    location /lua {\n        content_by_lua \"ngx.say('Hi'); ngx.eof(); ngx.exit(0);ngx.say('world')\";\n    }\n--- request\nGET /lua\n--- error_code: 200\n--- response_body\nHi\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: pcall safe\n--- config\n    location /lua {\n        content_by_lua '\n            local function f ()\n                ngx.say(\"hello\")\n                ngx.exit(200)\n            end\n\n            pcall(f)\n            ngx.say(\"world\")\n        ';\n    }\n--- request\nGET /lua\n--- error_code: 200\n--- response_body\nhello\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: 501 Method Not Implemented\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.exit(501)\n        ';\n    }\n--- request\nGET /lua\n--- error_code: 501\n--- response_body_like: 501 (?:Method )?Not Implemented\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: 501 Method Not Implemented\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.exit(ngx.HTTP_METHOD_NOT_IMPLEMENTED)\n        ';\n    }\n--- request\nGET /lua\n--- error_code: 501\n--- response_body_like: 501 (?:Method )?Not Implemented\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: throw 403 after sending out headers with 200\n--- config\n    location /lua {\n        rewrite_by_lua '\n            ngx.send_headers()\n            ngx.say(\"Hello World\")\n            ngx.exit(403)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nHello World\n--- error_log\nattempt to set status 403 via ngx.exit after sending out the response status 200\n--- no_error_log\n[alert]\n\n\n\n=== TEST 15: throw 403 after sending out headers with 403\n--- config\n    location /lua {\n        rewrite_by_lua '\n            ngx.status = 403\n            ngx.send_headers()\n            ngx.say(\"Hello World\")\n            ngx.exit(403)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nHello World\n--- error_code: 403\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 16: throw 403 after sending out headers with 403 (HTTP 1.0 buffering)\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.status = 403\n            ngx.say(\"Hello World\")\n            ngx.exit(403)\n        ';\n    }\n--- request\nGET /t HTTP/1.0\n--- response_body\nHello World\n--- error_code: 403\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 17: throw 444 after sending out responses (HTTP 1.0)\n--- config\n    location /lua {\n        content_by_lua \"\n            ngx.say('ok');\n            return ngx.exit(444)\n        \";\n    }\n--- request\nGET /lua HTTP/1.0\n--- ignore_response\n--- log_level: debug\n--- no_error_log\nlua sending HTTP 1.0 response headers\n[error]\n\n\n\n=== TEST 18: throw 499 after sending out responses (HTTP 1.0)\n--- config\n    location /lua {\n        content_by_lua \"\n            ngx.say('ok');\n            return ngx.exit(499)\n        \";\n    }\n--- request\nGET /lua HTTP/1.0\n--- ignore_response\n--- log_level: debug\n--- no_error_log\nlua sending HTTP 1.0 response headers\n[error]\n\n\n\n=== TEST 19: throw 408 after sending out responses (HTTP 1.0)\n--- config\n    location /lua {\n        content_by_lua \"\n            ngx.say('ok');\n            return ngx.exit(408)\n        \";\n    }\n--- request\nGET /lua HTTP/1.0\n--- ignore_response\n--- log_level: debug\n--- no_error_log\nlua sending HTTP 1.0 response headers\n[error]\n\n\n\n=== TEST 20: exit(201) with custom response body\n--- config\n    location = /t {\n        content_by_lua \"\n            ngx.status = 201\n            ngx.say('ok');\n            return ngx.exit(201)\n        \";\n    }\n--- request\n    GET /t\n--- ignore_response\n--- log_level: debug\n--- no_error_log\nlua sending HTTP 1.0 response headers\n[error]\n[alert]\n\n\n\n=== TEST 21: exit 403 in header filter\n--- config\n    location = /t {\n        content_by_lua \"ngx.say('hi');\";\n        header_filter_by_lua '\n            return ngx.exit(403)\n        ';\n    }\n--- request\nGET /t\n--- error_code: 403\n--- response_body_like: 403 Forbidden\n--- no_error_log\n[error]\n--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n\n\n\n=== TEST 22: exit 201 in header filter\n--- config\n    lingering_close always;\n    location = /t {\n        content_by_lua \"ngx.say('hi');\";\n        header_filter_by_lua '\n            return ngx.exit(201)\n        ';\n    }\n--- request\nGET /t\n--- error_code: 201\n--- response_body\n--- no_error_log\n[error]\n--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n\n\n\n=== TEST 23: exit both in header filter and content handler\n--- config\n    location = /t {\n        content_by_lua \"ngx.status = 201 ngx.say('hi') ngx.exit(201)\";\n        header_filter_by_lua '\n            return ngx.exit(201)\n        ';\n    }\n--- request\nGET /t\n--- error_code: 201\n--- stap2\n/*\nF(ngx_http_send_header) {\n    printf(\"=== %d\\n\", $r->headers_out->status)\n    print_ubacktrace()\n}\n*/\nF(ngx_http_lua_header_filter_inline) {\n    printf(\"=== %d\\n\", $r->headers_out->status)\n    print_ubacktrace()\n}\nF(ngx_http_lua_header_filter_by_chunk).return {\n    if ($return == -1) {\n        printf(\"====== header filter by chunk\\n\")\n        print_ubacktrace()\n    }\n}\n--- stap_out\n--- response_body\n--- no_error_log\n[error]\n[alert]\n--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n\n\n\n=== TEST 24: exit 444 in header filter\n--- config\n    location = /t {\n        content_by_lua \"ngx.say('hello world');\";\n        header_filter_by_lua '\n            return ngx.exit(444)\n        ';\n    }\n--- request\nGET /t\n--- error_code: 444\n--- response_body\n--- no_error_log\n[error]\n--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n\n\n\n=== TEST 25: 501 Method Not Implemented\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.exit(ngx.HTTP_NOT_IMPLEMENTED)\n        ';\n    }\n--- request\nGET /lua\n--- error_code: 501\n--- response_body_like: 501 (?:Method )?Not Implemented\n--- no_error_log\n[error]\n\n\n\n=== TEST 26: accepts NGX_OK\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.exit(ngx.OK)\n        }\n    }\n--- request\nGET /t\n--- response_body\n--- no_error_log\n[error]\n\n\n\n=== TEST 27: accepts NGX_ERROR\n--- no_http2\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.exit(ngx.ERROR)\n        }\n    }\n--- request\nGET /t\n--- error_code:\n--- response_body\n--- no_error_log\n[error]\n--- curl_error\ncurl: (95) HTTP/3 stream 0 reset by server\n\n\n\n=== TEST 28: accepts NGX_DECLINED\n--- no_http2\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.exit(ngx.DECLINED)\n        }\n    }\n--- request\nGET /t\n--- error_code:\n--- response_body\n--- no_error_log\n[error]\n--- curl_error\ncurl: (95) HTTP/3 stream 0 reset by server\n\n\n\n=== TEST 29: refuses NGX_AGAIN\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.exit(ngx.AGAIN)\n        }\n    }\n--- request\nGET /t\n--- error_code: 500\n--- response_body_like: 500 Internal Server Error\n--- error_log eval\nqr/\\[error\\] .*? bad argument to 'ngx.exit': does not accept NGX_AGAIN or NGX_DONE/\n\n\n\n=== TEST 30: refuses NGX_DONE\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.exit(ngx.DONE)\n        }\n    }\n--- request\nGET /t\n--- error_code: 500\n--- response_body_like: 500 Internal Server Error\n--- error_log eval\nqr/\\[error\\] .*? bad argument to 'ngx.exit': does not accept NGX_AGAIN or NGX_DONE/\n"
  },
  {
    "path": "t/006-escape.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 4);\n\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: escape uri in set_by_lua\n--- config\n    location /escape {\n        set_by_lua $res \"return ngx.escape_uri('a 你')\";\n        echo $res;\n    }\n--- request\nGET /escape\n--- response_body\na%20%E4%BD%A0\n\n\n\n=== TEST 2: unescape uri in set_by_lua\n--- config\n    location /unescape {\n        set_by_lua $res \"return ngx.unescape_uri('a%20%e4%bd%a0')\";\n        echo $res;\n    }\n--- request\nGET /unescape\n--- response_body\na 你\n\n\n\n=== TEST 3: escape uri in content_by_lua\n--- config\n    location /escape {\n        content_by_lua \"ngx.say(ngx.escape_uri('a 你'))\";\n    }\n--- request\nGET /escape\n--- response_body\na%20%E4%BD%A0\n\n\n\n=== TEST 4: unescape uri in content_by_lua\n--- config\n    location /unescape {\n        content_by_lua \"ngx.say(ngx.unescape_uri('a%20%e4%bd%a0'))\";\n    }\n--- request\nGET /unescape\n--- response_body\na 你\n\n\n\n=== TEST 5: escape uri in set_by_lua\n--- config\n    location /escape {\n        set_by_lua $res \"return ngx.escape_uri('a+b')\";\n        echo $res;\n    }\n--- request\nGET /escape\n--- response_body\na%2Bb\n\n\n\n=== TEST 6: escape uri in set_by_lua\n--- config\n    location /escape {\n        set_by_lua $res \"return ngx.escape_uri('\\\"a/b={}:<>;&[]\\\\\\\\^')\";\n        echo $res;\n    }\n--- request\nGET /escape\n--- response_body\n%22a%2Fb%3D%7B%7D%3A%3C%3E%3B%26%5B%5D%5C%5E\n\n\n\n=== TEST 7: escape uri in set_by_lua\n--- config\n    location /escape {\n        echo hello;\n        header_filter_by_lua '\n            ngx.header.baz = ngx.escape_uri(\" \")\n        ';\n    }\n--- request\nGET /escape\n--- response_headers\nbaz: %20\n--- response_body\nhello\n\n\n\n=== TEST 8: escape a string that cannot be escaped\n--- config\n    location /escape {\n        set_by_lua $res \"return ngx.escape_uri('abc')\";\n        echo $res;\n    }\n--- request\nGET /escape\n--- response_body\nabc\n\n\n\n=== TEST 9: escape an empty string that cannot be escaped\n--- config\n    location /escape {\n        set_by_lua $res \"return ngx.escape_uri('')\";\n        echo $res;\n    }\n--- request\nGET /escape\n--- response_body eval: \"\\n\"\n\n\n\n=== TEST 10: escape nil\n--- config\n    location /escape {\n        set_by_lua $res \"return ngx.escape_uri(nil)\";\n        echo \"[$res]\";\n    }\n--- request\nGET /escape\n--- response_body\n[]\n\n\n\n=== TEST 11: escape numbers\n--- config\n    location /escape {\n        set_by_lua $res \"return ngx.escape_uri(32)\";\n        echo \"[$res]\";\n    }\n--- request\nGET /escape\n--- response_body\n[32]\n\n\n\n=== TEST 12: unescape nil\n--- config\n    location = /t {\n        set_by_lua $res \"return ngx.unescape_uri(nil)\";\n        echo \"[$res]\";\n    }\n--- request\nGET /t\n--- response_body\n[]\n\n\n\n=== TEST 13: unescape numbers\n--- config\n    location = /t {\n        set_by_lua $res \"return ngx.unescape_uri(32)\";\n        echo \"[$res]\";\n    }\n--- request\nGET /t\n--- response_body\n[32]\n\n\n\n=== TEST 14: reserved chars\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(ngx.escape_uri(\"-_.!~*'()\"))\n            ngx.say(ngx.escape_uri(\",$@|`\"))\n        }\n    }\n--- request\nGET /lua\n--- response_body\n-_.!~*'()\n%2C%24%40%7C%60\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: escape type argument\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(ngx.escape_uri(\"https://www.google.com\", 0))\n            ngx.say(ngx.escape_uri(\"https://www.google.com/query?q=test\", 0))\n            ngx.say(ngx.escape_uri(\"https://www.google.com/query?\\r\\nq=test\", 0))\n            ngx.say(ngx.escape_uri(\"-_.~!*'();:@&=+$,/?#\", 0))\n            ngx.say(ngx.escape_uri(\"<>[]{}\\\\\\\" \", 0))\n        }\n    }\n--- request\nGET /lua\n--- response_body\nhttps://www.google.com\nhttps://www.google.com/query%3Fq=test\nhttps://www.google.com/query%3F%0D%0Aq=test\n-_.~!*'();:@&=+$,/%3F%23\n<>[]{}\\\"%20\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: escape type argument\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(ngx.escape_uri(\"https://www.google.com/?t=abc@ :\", 0))\n            ngx.say(ngx.escape_uri(\"https://www.google.com/?t=abc@ :\", 1))\n            ngx.say(ngx.escape_uri(\"https://www.google.com/?t=abc@ :\", 2))\n            ngx.say(ngx.escape_uri(\"https://www.google.com/?t=abc@ :\", 3))\n            ngx.say(ngx.escape_uri(\"https://www.google.com/?t=abc@ :\", 4))\n            ngx.say(ngx.escape_uri(\"https://www.google.com/?t=abc@ :\", 5))\n            ngx.say(ngx.escape_uri(\"https://www.google.com/?t=abc@ :\", 6))\n        }\n    }\n--- request\nGET /lua\n--- response_body\nhttps://www.google.com/%3Ft=abc@%20:\nhttps://www.google.com/%3Ft=abc@%20:\nhttps%3A%2F%2Fwww.google.com%2F%3Ft%3Dabc%40%20%3A\nhttps://www.google.com/?t=abc@%20:\nhttps://www.google.com/?t=abc@%20:\nhttps://www.google.com/?t=abc@%20:\nhttps://www.google.com/?t=abc@%20:\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: escape type out of range\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(ngx.escape_uri(\"https://www.google.com\", -1))\n        }\n    }\n--- request\nGET /lua\n--- error_code: 500\n--- error_log eval\nqr/\\[error\\] \\d+#\\d+: \\*\\d+ lua entry thread aborted: runtime error: \"type\" \\-1 out of range/\n\n\n\n=== TEST 18: escape type out of range\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(ngx.escape_uri(\"https://www.google.com\", 10))\n        }\n    }\n--- request\nGET /lua\n--- error_code: 500\n--- error_log eval\nqr/\\[error\\] \\d+#\\d+: \\*\\d+ lua entry thread aborted: runtime error: \"type\" 10 out of range/\n\n\n\n=== TEST 19: escape type not integer\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(ngx.escape_uri(\"https://www.google.com\", true))\n        }\n    }\n--- request\nGET /lua\n--- error_code: 500\n--- error_log eval\nqr/\\[error\\] \\d+#\\d+: \\*\\d+ lua entry thread aborted: runtime error: \"type\" is not a number/\n\n\n\n=== TEST 20: invalid unescape sequences\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(ngx.unescape_uri(\"%ua%%20%au\"))\n        }\n    }\n--- request\nGET /lua\n--- response_body\n%ua% %au\n\n\n\n=== TEST 21: invalid unescape sequences where remain length less than 2\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(ngx.unescape_uri(\"%a\")) -- first character is good\n            ngx.say(ngx.unescape_uri(\"%u\")) -- first character is bad\n            ngx.say(ngx.unescape_uri(\"%\"))\n            ngx.say(ngx.unescape_uri(\"good%20job%\"))\n        }\n    }\n--- request\nGET /lua\n--- response_body\n%a\n%u\n%\ngood job%\n"
  },
  {
    "path": "t/007-md5.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: set md5 hello\n--- config\n    location = /md5 {\n        content_by_lua 'ngx.say(ngx.md5(\"hello\"))';\n    }\n--- request\nGET /md5\n--- response_body\n5d41402abc4b2a76b9719d911017c592\n\n\n\n=== TEST 2: nil string to ngx.md5\n--- config\n    location = /md5 {\n        content_by_lua 'ngx.say(ngx.md5(nil))';\n    }\n--- request\nGET /md5\n--- response_body\nd41d8cd98f00b204e9800998ecf8427e\n\n\n\n=== TEST 3: null string to ngx.md5\n--- config\n    location /md5 {\n        content_by_lua 'ngx.say(ngx.md5(\"\"))';\n    }\n--- request\nGET /md5\n--- response_body\nd41d8cd98f00b204e9800998ecf8427e\n\n\n\n=== TEST 4: use ngx.md5 in set_by_lua\n--- config\n    location = /md5 {\n        set_by_lua $a 'return ngx.md5(\"hello\")';\n        echo $a;\n    }\n--- request\nGET /md5\n--- response_body\n5d41402abc4b2a76b9719d911017c592\n\n\n\n=== TEST 5: use ngx.md5 in set_by_lua (nil)\n--- config\n    location = /md5 {\n        set_by_lua $a 'return ngx.md5(nil)';\n        echo $a;\n    }\n--- request\nGET /md5\n--- response_body\nd41d8cd98f00b204e9800998ecf8427e\n\n\n\n=== TEST 6: use ngx.md5 in set_by_lua (null string)\n--- config\n    location /md5 {\n        set_by_lua $a 'return ngx.md5(\"\")';\n        echo $a;\n    }\n--- request\nGET /md5\n--- response_body\nd41d8cd98f00b204e9800998ecf8427e\n\n\n\n=== TEST 7: md5(number)\n--- config\n    location = /md5 {\n        content_by_lua 'ngx.say(ngx.md5(45))';\n    }\n--- request\nGET /md5\n--- response_body\n6c8349cc7260ae62e3b1396831a8398f\n"
  },
  {
    "path": "t/008-today.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: use ngx.today in content_by_lua\n--- config\n    location = /today {\n        content_by_lua 'ngx.say(ngx.today())';\n    }\n--- request\nGET /today\n--- response_body_like: ^\\d{4}-\\d{2}-\\d{2}$\n\n\n\n=== TEST 2: use ngx.today in set_by_lua\n--- config\n    location = /today {\n        set_by_lua $a 'return ngx.today()';\n        echo $a;\n    }\n--- request\nGET /today\n--- response_body_like: ^\\d{4}-\\d{2}-\\d{2}$\n"
  },
  {
    "path": "t/009-log.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('debug'); # to ensure any log-level can be outputted\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 4);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: test log-level STDERR\n--- config\n    location /log {\n        content_by_lua '\n            ngx.say(\"before log\")\n            ngx.log(ngx.STDERR, \"hello, log\", 1234, 3.14159)\n            ngx.say(\"after log\")\n        ';\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):3: hello, log12343.14159/\n\n\n\n=== TEST 2: test log-level EMERG\n--- config\n    location /log {\n        content_by_lua '\n            ngx.say(\"before log\")\n            ngx.log(ngx.EMERG, \"hello, log\", 1234, 3.14159)\n            ngx.say(\"after log\")\n        ';\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[emerg\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):3: hello, log12343.14159/\n\n\n\n=== TEST 3: test log-level ALERT\n--- config\n    location /log {\n        content_by_lua '\n            ngx.say(\"before log\")\n            ngx.log(ngx.ALERT, \"hello, log\", 1234, 3.14159)\n            ngx.say(\"after log\")\n        ';\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[alert\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):3: hello, log12343.14159/\n\n\n\n=== TEST 4: test log-level CRIT\n--- config\n    location /log {\n        content_by_lua '\n            ngx.say(\"before log\")\n            ngx.log(ngx.CRIT, \"hello, log\", 1234, 3.14159)\n            ngx.say(\"after log\")\n        ';\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[crit\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):3: hello, log12343.14159/\n\n\n\n=== TEST 5: test log-level ERR\n--- config\n    location /log {\n        content_by_lua '\n            ngx.say(\"before log\")\n            ngx.log(ngx.ERR, \"hello, log\", 1234, 3.14159)\n            ngx.say(\"after log\")\n        ';\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[error\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):3: hello, log12343.14159/\n\n\n\n=== TEST 6: test log-level WARN\n--- config\n    location /log {\n        content_by_lua '\n            ngx.say(\"before log\")\n            ngx.log(ngx.WARN, \"hello, log\", 1234, 3.14159)\n            ngx.say(\"after log\")\n        ';\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[warn\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):3: hello, log12343.14159/\n\n\n\n=== TEST 7: test log-level NOTICE\n--- config\n    location /log {\n        content_by_lua '\n            ngx.say(\"before log\")\n            ngx.log(ngx.NOTICE, \"hello, log\", 1234, 3.14159)\n            ngx.say(\"after log\")\n        ';\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[notice\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):3: hello, log12343.14159/\n\n\n\n=== TEST 8: test log-level INFO\n--- config\n    location /log {\n        content_by_lua '\n            ngx.say(\"before log\")\n            ngx.log(ngx.INFO, \"hello, log\", 1234, 3.14159)\n            ngx.say(\"after log\")\n        ';\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[info\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):3: hello, log12343.14159/\n\n\n\n=== TEST 9: test log-level DEBUG\n--- config\n    location /log {\n        content_by_lua '\n            ngx.say(\"before log\")\n            ngx.log(ngx.DEBUG, \"hello, log\", 1234, 3.14159)\n            ngx.say(\"after log\")\n        ';\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[debug\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):3: hello, log12343.14159/\n\n\n\n=== TEST 10: regression test print()\n--- config\n    location /log {\n        content_by_lua '\n            ngx.say(\"before log\")\n            print(\"hello, log\", 1234, 3.14159)\n            ngx.say(\"after log\")\n        ';\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[notice\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):3: hello, log12343.14159/\n\n\n\n=== TEST 11: print(nil)\n--- config\n    location /log {\n        content_by_lua '\n            print()\n            print(nil)\n            print(\"nil: \", nil)\n            ngx.say(\"hi\");\n        ';\n    }\n--- request\nGET /log\n--- response_body\nhi\n--- error_log eval\n[\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):2: ,/,\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):3: nil,/,\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):4: nil: nil,/,\n]\n\n\n\n=== TEST 12: ngx.log in set_by_lua\n--- config\n    location /log {\n        set_by_lua $a '\n            ngx.log(ngx.ERR, \"HELLO\")\n            return 32;\n        ';\n        echo $a;\n    }\n--- request\nGET /log\n--- response_body\n32\n--- error_log eval\nqr/\\[error\\] \\S+: \\S+ \\[lua\\] set_by_lua\\(nginx.conf:43\\):2: HELLO,/\n\n\n\n=== TEST 13: test booleans and nil\n--- config\n    location /log {\n        set_by_lua $a '\n            ngx.log(ngx.ERR, true, false, nil)\n            return 32;\n        ';\n        echo $a;\n    }\n--- request\nGET /log\n--- response_body\n32\n--- error_log eval\nqr/\\[error\\] \\S+: \\S+ \\[lua\\] set_by_lua\\(nginx.conf:43\\):2: truefalsenil,/\n\n\n\n=== TEST 14: test table with metamethod\n--- config\n    location /log {\n        content_by_lua_block {\n            ngx.say(\"before log\")\n            local t = setmetatable({v = \"value\"}, {\n                __tostring = function(self)\n                    return \"tostring \"..self.v\n                end\n            })\n            ngx.log(ngx.ERR, t)\n            ngx.say(\"after log\")\n        }\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[error\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):8: tostring value,/\n\n\n\n=== TEST 15: test table without metamethod\n--- config\n    location /log {\n        content_by_lua_block {\n            ngx.log(ngx.ERR, {})\n            ngx.say(\"done\")\n        }\n    }\n--- request\nGET /log\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad argument #1 to 'log' (expected table to have __tostring metamethod)\n\n\n\n=== TEST 16: test tables mixed with other types\n--- config\n    location /log {\n        content_by_lua_block {\n            ngx.say(\"before log\")\n            local t = setmetatable({v = \"value\"}, {\n                __tostring = function(self)\n                    return \"tostring: \"..self.v\n                end\n            })\n            ngx.log(ngx.ERR, t, \" hello \", t)\n            ngx.say(\"after log\")\n        }\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[error\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):8: tostring: value hello tostring: value,/\n\n\n\n=== TEST 17: test print with tables\n--- config\n    location /log {\n        content_by_lua_block {\n            ngx.say(\"before log\")\n            local t = setmetatable({v = \"value\"}, {\n                __tostring = function(self)\n                    return \"tostring: \"..self.v\n                end\n            })\n            print(t, \" hello \", t)\n            ngx.say(\"after log\")\n        }\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[notice\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):8: tostring: value hello tostring: value,/\n\n\n\n=== TEST 18: print() in header filter\n--- config\n    location /log {\n        header_filter_by_lua '\n            print(\"hello world\")\n            ngx.header.foo = 32\n        ';\n        echo hi;\n    }\n--- request\nGET /log\n--- response_headers\nfoo: 32\n--- error_log eval\nqr/\\[notice\\] .*? \\[lua\\] header_filter_by_lua\\(nginx.conf:43\\):2: hello world/\n--- response_body\nhi\n\n\n\n=== TEST 19: ngx.log in header filter\n--- config\n    location /log {\n        header_filter_by_lua '\n            ngx.log(ngx.ERR, \"howdy, lua!\")\n            ngx.header.foo = 32\n        ';\n        echo hi;\n    }\n--- request\nGET /log\n--- response_headers\nfoo: 32\n--- response_body\nhi\n--- error_log eval\nqr/\\[error\\] .*? \\[lua\\] header_filter_by_lua\\(nginx.conf:43\\):2: howdy, lua!/\n\n\n\n=== TEST 20: ngx.log big data\n--- config\n    location /log {\n        content_by_lua '\n            ngx.log(ngx.ERR, \"a\" .. string.rep(\"h\", 1970) .. \"b\")\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /log\n--- response_headers\n--- error_log eval\n[qr/ah{1970}b/]\n\n\n\n=== TEST 21: ngx.log in Lua function calls & inlined lua\n--- config\n    location /log {\n        content_by_lua '\n            local foo\n            local bar\n            function foo()\n                bar()\n            end\n\n            function bar()\n                ngx.log(ngx.ERR, \"hello, log\", 1234, 3.14159)\n            end\n\n            foo()\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /log\n--- response_body\ndone\n--- error_log eval\nqr/\\[error\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):9: bar\\(\\): hello, log12343.14159/\n\n\n\n=== TEST 22: ngx.log in Lua function tail-calls & inlined lua\n--- config\n    location /log {\n        content_by_lua '\n            local foo\n            local bar\n            function foo()\n                return bar(5)\n            end\n\n            function bar(n)\n                if n < 1 then\n                    ngx.log(ngx.ERR, \"hello, log\", 1234, 3.14159)\n                    return n\n                end\n\n                return bar(n - 1)\n            end\n\n            foo()\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /log\n--- response_body\ndone\n--- error_log eval\nqr/\\[error\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):10:(?: foo\\(\\):)? hello, log12343.14159/\n\n\n\n=== TEST 23: ngx.log in Lua files\n--- config\n    location /log {\n        content_by_lua_file 'html/test.lua';\n    }\n--- user_files\n>>> test.lua\nlocal foo\nlocal bar\nfunction foo()\n    bar()\nend\n\nfunction bar()\n    ngx.log(ngx.ERR, \"hello, log\", 1234, 3.14159)\nend\n\nfoo()\nngx.say(\"done\")\n\n--- request\nGET /log\n--- response_body\ndone\n--- error_log eval\nqr/\\[error\\] \\S+: \\S+ \\[lua\\] test.lua:8: bar\\(\\): hello, log12343.14159/\n\n\n\n=== TEST 24: ngx.log with bad levels (ngx.ERROR, -1)\n--- config\n    location /log {\n        content_by_lua '\n            ngx.log(ngx.ERROR, \"hello lua\")\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /log\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad log level: -1\n\n\n\n=== TEST 25: ngx.log with bad levels (9)\n--- config\n    location /log {\n        content_by_lua '\n            ngx.log(9, \"hello lua\")\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /log\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad log level: 9\n\n\n\n=== TEST 26: \\0 in the log message\n--- config\n    location = /t {\n        content_by_lua '\n            ngx.log(ngx.WARN, \"hello\\\\0world\")\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /t\n--- response_body\nok\n--- no_error_log\n[error]\n--- error_log eval\n\"2: hello\\0world, client: \"\n\n\n\n=== TEST 27: test log-level STDERR\nNote: maximum number of digits after the decimal-point character is 13\n--- config\n    location /log {\n        content_by_lua_block {\n            ngx.say(\"before log\")\n            ngx.log(ngx.STDERR, 3.14159265357939723846)\n            ngx.say(\"after log\")\n        }\n    }\n--- request\nGET /log\n--- response_body\nbefore log\nafter log\n--- error_log eval\nqr/\\[\\] \\S+: \\S+ \\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):3: 3.1415926535794/\n"
  },
  {
    "path": "t/010-request_body.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('debug'); # to ensure any log-level can be outputted\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 2);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: test reading request body\n--- config\n    location /echo_body {\n        lua_need_request_body on;\n        content_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"hello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n\n\n\n=== TEST 2: test not reading request body\n--- config\n    location /echo_body {\n        lua_need_request_body off;\n        content_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"nil\"\n\n\n\n=== TEST 3: test default setting (not reading request body)\n--- config\n    location /echo_body {\n        content_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"nil\"\n\n\n\n=== TEST 4: test main conf\n--- http_config\n    lua_need_request_body on;\n--- config\n    location /echo_body {\n        content_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"hello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n\n\n\n=== TEST 5: test server conf\n--- config\n    lua_need_request_body on;\n\n    location /echo_body {\n        content_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"hello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n\n\n\n=== TEST 6: test override main conf\n--- http_config\n    lua_need_request_body on;\n--- config\n    location /echo_body {\n        lua_need_request_body off;\n        content_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"nil\"\n\n\n\n=== TEST 7: test override server conf\n--- config\n    lua_need_request_body on;\n\n    location /echo_body {\n        lua_need_request_body off;\n        content_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"nil\"\n\n\n\n=== TEST 8: test override server conf\n--- config\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/hi;\n    }\n    location /hi {\n        echo_request_body;\n    }\n    location /echo_body {\n        lua_need_request_body off;\n        content_by_lua '\n            ngx.say(ngx.var.request_body or \"nil\")\n            local res = ngx.location.capture(\n                \"/proxy\",\n                { method = ngx.HTTP_POST,\n                  body = ngx.var.request_body })\n\n            ngx.say(res.status)\n        ';\n    }\n--- request eval\n\"POST /echo_body\n\"\n--- response_body\nnil\n200\n\n\n\n=== TEST 9: empty POST body\n--- config\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/hi;\n    }\n    location /hi {\n        echo_request_body;\n    }\n    location /echo_body {\n        lua_need_request_body on;\n        content_by_lua '\n            ngx.say(ngx.var.request_body or \"nil\")\n            local res = ngx.location.capture(\n                \"/proxy\",\n                { method = ngx.HTTP_POST,\n                  body = ngx.var.request_body })\n\n            ngx.say(res.status)\n        ';\n    }\n--- request eval\n\"POST /echo_body\n\"\n--- response_body\nnil\n200\n\n\n\n=== TEST 10: on disk request body\n--- config\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/hi;\n    }\n    location /hi {\n        echo_request_body;\n    }\n    location /echo_body {\n        lua_need_request_body on;\n\n        client_max_body_size 100k;\n        client_body_buffer_size 1;\n        sendfile on;\n\n        content_by_lua '\n            local res = ngx.location.capture(\n                \"/proxy\",\n                { method = ngx.HTTP_POST,\n                  body = ngx.var.request_body })\n            ngx.print(res.body)\n        ';\n    }\n--- request eval\n\"POST /echo_body\n\" . ('a' x 1024)\n--- response_body chomp\n\n\n\n=== TEST 11: no modify main request content-length\n--- config\n    location /foo {\n        content_by_lua '\n            ngx.location.capture(\"/other\", {body = \"hello\"})\n            ngx.say(ngx.req.get_headers()[\"Content-Length\"] or \"nil\")\n        ';\n    }\n    location /other {\n        echo hi;\n    }\n--- request\nPOST /foo\nhi\n--- response_body\n2\n\n\n\n=== TEST 12: Expect: 100-Continue\n--- config\n    location /echo_body {\n        lua_need_request_body on;\n        content_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request\nPOST /echo_body\nhello world\n--- more_headers\nExpect: 100-Continue\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\nhttp finalize request: 500, \"/echo_body?\" a:1, c:2\nhttp finalize request: 500, \"/echo_body?\" a:1, c:0\n--- log_level: debug\n--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 13: test reading the first n bytes of request body\n--- config\n    location /echo_body {\n        lua_need_request_body on;\n        content_by_lua_block {\n            local data = ngx.req.get_body_data(1)\n            ngx.say(data)\n        }\n    }\n--- request\nPOST /echo_body\nhello\n--- response_body\nh\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3}\n"
  },
  {
    "path": "t/011-md5_bin.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n\n#md5_bin_bin is hard to test, so convert it to hex mode\n\n__DATA__\n\n=== TEST 1: set md5_bin hello ????xxoo\n--- config\n    location = /md5_bin {\n        content_by_lua 'local a = string.gsub(ngx.md5_bin(\"hello\"), \".\", function (c)\n                    return string.format(\"%02x\", string.byte(c))\n                end); ngx.say(a)';\n    }\n--- request\nGET /md5_bin\n--- response_body\n5d41402abc4b2a76b9719d911017c592\n\n\n\n=== TEST 2: set md5_bin hello ????xxoo\n--- config\n    location = /md5_bin {\n        content_by_lua 'ngx.say(string.len(ngx.md5_bin(\"hello\")))';\n    }\n--- request\nGET /md5_bin\n--- response_body\n16\n\n\n\n=== TEST 3: set md5_bin hello\n--- config\n    location = /md5_bin {\n        content_by_lua '\n            local s = ngx.md5_bin(\"hello\")\n            s = string.gsub(s, \".\", function (c)\n                    return string.format(\"%02x\", string.byte(c))\n                end)\n            ngx.say(s)\n        ';\n    }\n--- request\nGET /md5_bin\n--- response_body\n5d41402abc4b2a76b9719d911017c592\n\n\n\n=== TEST 4: nil string to ngx.md5_bin\n--- config\n    location = /md5_bin {\n        content_by_lua '\n            local s = ngx.md5_bin(nil)\n            s = string.gsub(s, \".\", function (c)\n                    return string.format(\"%02x\", string.byte(c))\n                end)\n            ngx.say(s)\n        ';\n    }\n--- request\nGET /md5_bin\n--- response_body\nd41d8cd98f00b204e9800998ecf8427e\n\n\n\n=== TEST 5: null string to ngx.md5_bin\n--- config\n    location /md5_bin {\n        content_by_lua '\n            local s = ngx.md5_bin(\"\")\n            s = string.gsub(s, \".\", function (c)\n                    return string.format(\"%02x\", string.byte(c))\n                end)\n            ngx.say(s)\n        ';\n    }\n--- request\nGET /md5_bin\n--- response_body\nd41d8cd98f00b204e9800998ecf8427e\n\n\n\n=== TEST 6: use ngx.md5_bin in set_by_lua\n--- config\n    location = /md5_bin {\n        set_by_lua $a 'return string.gsub(ngx.md5_bin(\"hello\"), \".\", function (c)\n                    return string.format(\"%02x\", string.byte(c))\n                end)';\n        echo $a;\n    }\n--- request\nGET /md5_bin\n--- response_body\n5d41402abc4b2a76b9719d911017c592\n\n\n\n=== TEST 7: use ngx.md5_bin in set_by_lua (nil)\n--- config\n    location = /md5_bin {\n        set_by_lua $a '\n            local s = ngx.md5_bin(nil)\n            s = string.gsub(s, \".\", function (c)\n                    return string.format(\"%02x\", string.byte(c))\n                end)\n            return s\n        ';\n        echo $a;\n    }\n--- request\nGET /md5_bin\n--- response_body\nd41d8cd98f00b204e9800998ecf8427e\n\n\n\n=== TEST 8: use ngx.md5_bin in set_by_lua (null string)\n--- config\n    location /md5_bin {\n        set_by_lua $a '\n            local s = ngx.md5_bin(\"\")\n            s = string.gsub(s, \".\", function (c)\n                    return string.format(\"%02x\", string.byte(c))\n                end)\n            return s\n        ';\n        echo $a;\n    }\n--- request\nGET /md5_bin\n--- response_body\nd41d8cd98f00b204e9800998ecf8427e\n\n\n\n=== TEST 9: md5_bin(number)\n--- config\n    location = /t {\n        content_by_lua '\n            local s = ngx.md5_bin(45)\n            s = string.gsub(s, \".\", function (c)\n                    return string.format(\"%02x\", string.byte(c))\n                end)\n            ngx.say(s)\n\n        ';\n    }\n--- request\nGET /t\n--- response_body\n6c8349cc7260ae62e3b1396831a8398f\n"
  },
  {
    "path": "t/012-now.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 2);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: use ngx.localtime in content_by_lua\n--- config\n    location = /now {\n        content_by_lua 'ngx.say(ngx.localtime())';\n    }\n--- request\nGET /now\n--- response_body_like: ^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$\n\n\n\n=== TEST 2: use ngx.localtime in set_by_lua\n--- config\n    location = /now {\n        set_by_lua $a 'return ngx.localtime()';\n        echo $a;\n    }\n--- request\nGET /now\n--- response_body_like: ^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$\n\n\n\n=== TEST 3: use ngx.time in set_by_lua\n--- config\n    location = /time {\n        set_by_lua $a 'return ngx.time()';\n        echo $a;\n    }\n--- request\nGET /time\n--- response_body_like: ^\\d{10,}$\n\n\n\n=== TEST 4: use ngx.time in content_by_lua\n--- config\n    location = /time {\n        content_by_lua 'ngx.say(ngx.time())';\n    }\n--- request\nGET /time\n--- response_body_like: ^\\d{10,}$\n\n\n\n=== TEST 5: use ngx.time in content_by_lua\n--- config\n    location = /time {\n        content_by_lua '\n            ngx.say(ngx.time())\n            ngx.say(ngx.localtime())\n            ngx.say(ngx.utctime())\n            ngx.say(ngx.cookie_time(ngx.time()))\n        ';\n    }\n--- request\nGET /time\n--- response_body_like chomp\n^\\d{10,}\n\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\n\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\n\\w+, .*? GMT$\n\n\n\n=== TEST 6: use ngx.now in set_by_lua\n--- config\n    location = /time {\n        set_by_lua $a 'return ngx.now()';\n        echo $a;\n    }\n--- request\nGET /time\n--- response_body_like: ^\\d{10,}(\\.\\d{1,3})?$\n\n\n\n=== TEST 7: use ngx.now in content_by_lua\n--- config\n    location = /time {\n        content_by_lua 'ngx.say(ngx.now())';\n    }\n--- request\nGET /time\n--- response_body_like: ^\\d{10,}(\\.\\d{1,3})?$\n\n\n\n=== TEST 8: use ngx.update_time & ngx.now in content_by_lua\n--- config\n    location = /time {\n        content_by_lua '\n            ngx.update_time()\n            ngx.say(ngx.now())\n        ';\n    }\n--- request\nGET /time\n--- response_body_like: ^\\d{10,}(\\.\\d{1,3})?$\n"
  },
  {
    "path": "t/013-base64.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 4);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: base64 encode hello\n--- config\n    location = /encode_base64 {\n        content_by_lua 'ngx.say(ngx.encode_base64(\"hello\"))';\n    }\n--- request\nGET /encode_base64\n--- response_body\naGVsbG8=\n\n\n\n=== TEST 2: nil string to ngx.encode_base64\n--- config\n    location = /encode_base64 {\n        content_by_lua 'ngx.say(\"left\" .. ngx.encode_base64(nil) .. \"right\")';\n    }\n--- request\nGET /encode_base64\n--- response_body\nleftright\n\n\n\n=== TEST 3: null string to ngx.encode_base64\n--- config\n    location = /encode_base64 {\n        content_by_lua 'ngx.say(\"left\" .. ngx.encode_base64(\"\") .. \"right\")';\n    }\n--- request\nGET /encode_base64\n--- response_body\nleftright\n\n\n\n=== TEST 4: use ngx.encode_base64 in set_by_lua\n--- config\n    location = /encode_base64 {\n        set_by_lua $a 'return ngx.encode_base64(\"hello\")';\n        echo $a;\n    }\n--- request\nGET /encode_base64\n--- response_body\naGVsbG8=\n\n\n\n=== TEST 5: use ngx.encode_base64 in set_by_lua (nil)\n--- config\n    location = /encode_base64 {\n        set_by_lua $a 'return \"left\" .. ngx.encode_base64(nil) .. \"right\"';\n        echo $a;\n    }\n--- request\nGET /encode_base64\n--- response_body\nleftright\n\n\n\n=== TEST 6: use ngx.encode_base64 in set_by_lua (null string)\n--- config\n    location /encode_base64 {\n        set_by_lua $a 'return \"left\" .. ngx.encode_base64(\"\") .. \"right\"';\n        echo $a;\n    }\n--- request\nGET /encode_base64\n--- response_body\nleftright\n\n\n\n=== TEST 7: base64 encode hello\n--- config\n    location = /decode_base64 {\n        content_by_lua 'ngx.say(ngx.decode_base64(\"aGVsbG8=\"))';\n    }\n--- request\nGET /decode_base64\n--- response_body\nhello\n\n\n\n=== TEST 8: null string to ngx.decode_base64\n--- config\n    location = /decode_base64 {\n        content_by_lua 'ngx.say(\"left\" .. ngx.decode_base64(\"\") .. \"right\")';\n    }\n--- request\nGET /decode_base64\n--- response_body\nleftright\n\n\n\n=== TEST 9: use ngx.decode_base64 in set_by_lua\n--- config\n    location = /decode_base64 {\n        set_by_lua $a 'return ngx.decode_base64(\"aGVsbG8=\")';\n        echo $a;\n    }\n--- request\nGET /decode_base64\n--- response_body\nhello\n\n\n\n=== TEST 10: use ngx.decode_base64 in set_by_lua (nil)\n--- config\n    location = /decode_base64 {\n        set_by_lua $a 'return \"left\" .. ngx.decode_base64(nil) .. \"right\"';\n        echo $a;\n    }\n--- request\nGET /decode_base64\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nstring argument only\n\n\n\n=== TEST 11: use ngx.decode_base64 in set_by_lua (null string)\n--- config\n    location /decode_base64 {\n        set_by_lua $a 'return \"left\" .. ngx.decode_base64(\"\") .. \"right\"';\n        echo $a;\n    }\n--- request\nGET /decode_base64\n--- response_body\nleftright\n\n\n\n=== TEST 12: base64 encode number\n--- config\n    location = /t {\n        content_by_lua 'ngx.say(ngx.encode_base64(32))';\n    }\n--- request\nGET /t\n--- response_body\nMzI=\n\n\n\n=== TEST 13: base64 decode number\n--- config\n    location = /t {\n        content_by_lua 'ngx.say(ngx.decode_base64(32))';\n    }\n--- request\nGET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nstring argument only\n\n\n\n=== TEST 14: base64 decode error\n--- config\n    location = /t {\n        content_by_lua 'ngx.say(ngx.decode_base64(\"^*~\"))';\n    }\n--- request\nGET /t\n--- response_body\nnil\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: base64 encode without padding (explicit true to no_padding)\n--- config\n    location = /t {\n        content_by_lua 'ngx.say(ngx.encode_base64(\"hello\", true))';\n    }\n--- request\nGET /t\n--- response_body\naGVsbG8\n\n\n\n=== TEST 16: base64 encode short string\n--- config\n    location = /t {\n        content_by_lua 'ngx.say(ngx.encode_base64(\"w\"))';\n    }\n--- request\nGET /t\n--- response_body\ndw==\n\n\n\n=== TEST 17: base64 encode short string with padding (explicit false to no_padding)\n--- config\n    location = /t {\n        content_by_lua 'ngx.say(ngx.encode_base64(\"w\", false))';\n    }\n--- request\nGET /t\n--- response_body\ndw==\n\n\n\n=== TEST 18: base64 encode with wrong 2nd parameter\n--- config\n    location = /t {\n        content_by_lua 'ngx.say(ngx.encode_base64(\"w\", 0))';\n    }\n--- request\nGET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/bad argument \\#2 to 'encode_base64' \\(boolean expected, got number\\)|\\[error\\] .*? bad no_padding: boolean expected, got number/\n"
  },
  {
    "path": "t/014-bugs.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\nlog_level('debug');\n\nrepeat_each(3);\n\n# NB: the shutdown_error_log block is independent from repeat times\nplan tests => repeat_each() * (blocks() * 2 + 41);\n\nour $HtmlDir = html_dir;\n#warn $html_dir;\n\n$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir;\n$ENV{TEST_NGINX_REDIS_PORT} ||= 6379;\n\n#no_diff();\n#no_long_string();\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\n#no_shuffle();\nno_long_string();\n\nsub read_file {\n    my $infile = shift;\n    open my $in, $infile\n        or die \"cannot open $infile for reading: $!\";\n    my $cert = do { local $/; <$in> };\n    close $in;\n    $cert;\n}\n\nour $TestCertificate = read_file(\"t/cert/test.crt\");\nour $TestCertificateKey = read_file(\"t/cert/test.key\");\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /load {\n        content_by_lua '\n            package.loaded.foo = nil;\n            local foo = require \"foo\";\n            foo.hi()\n        ';\n    }\n--- request\nGET /load\n--- user_files\n>>> foo.lua\nmodule(..., package.seeall);\n\nfunction foo ()\n    return 1\n    return 2\nend\n--- error_code: 500\n--- response_body_like: 500 Internal Server Error\n\n\n\n=== TEST 2: sanity\n--- http_config\nlua_package_cpath '/home/agentz/rpm/BUILD/lua-yajl-1.1/build/?.so;/home/lz/luax/?.so;./?.so';\n--- config\n    location = '/report/listBidwordPrices4lzExtra.htm' {\n        content_by_lua '\n            local yajl = require \"yajl\"\n            local w = ngx.var.arg_words\n            w = ngx.unescape_uri(w)\n            local r = {}\n            print(\"start for\")\n            for id in string.gmatch(w, \"%d+\") do\n                 r[id] = -1\n            end\n            print(\"end for, start yajl\")\n            ngx.print(yajl.to_string(r))\n            print(\"end yajl\")\n        ';\n    }\n--- request\nGET /report/listBidwordPrices4lzExtra.htm?words=123,156,2532\n--- response_body\n--- SKIP\n\n\n\n=== TEST 3: sanity\n--- config\n    location = /memc {\n        #set $memc_value 'hello';\n        set $memc_value $arg_v;\n        set $memc_cmd $arg_c;\n        set $memc_key $arg_k;\n        #set $memc_value hello;\n\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n        #echo $memc_value;\n    }\n    location = /echo {\n        echo_location '/memc?c=get&k=foo';\n        echo_location '/memc?c=set&k=foo&v=hello';\n        echo_location '/memc?c=get&k=foo';\n    }\n    location = /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/memc?c=get&k=foo&v=\")\n            ngx.say(\"1: \", res.body)\n\n            res = ngx.location.capture(\"/memc?c=set&k=foo&v=bar\");\n            ngx.say(\"2: \", res.body);\n\n            res = ngx.location.capture(\"/memc?c=get&k=foo\")\n            ngx.say(\"3: \", res.body);\n        ';\n    }\n--- request\nGET /main\n--- response_body_like: 3: bar$\n\n\n\n=== TEST 4: capture works for subrequests with internal redirects\n--- config\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/\")\n            ngx.say(res.status)\n            ngx.print(res.body)\n        ';\n    }\n--- request\n    GET /lua\n--- response_body_like chop\n200\n.*It works\n--- SKIP\n\n\n\n=== TEST 5: disk file bufs not working\n--- config\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/test.lua\")\n            ngx.say(res.status)\n            ngx.print(res.body)\n        ';\n    }\n--- user_files\n>>> test.lua\nprint(\"Hello, world\")\n--- request\n    GET /lua\n--- response_body\n200\nprint(\"Hello, world\")\n\n\n\n=== TEST 6: print lua empty strings\n--- config\n    location /lua {\n        content_by_lua 'ngx.print(\"\") ngx.flush() ngx.print(\"Hi\")';\n    }\n--- request\nGET /lua\n--- response_body chop\nHi\n\n\n\n=== TEST 7: say lua empty strings\n--- config\n    location /lua {\n        content_by_lua 'ngx.say(\"\") ngx.flush() ngx.print(\"Hi\")';\n    }\n--- request\nGET /lua\n--- response_body eval\n\"\nHi\"\n\n\n\n=== TEST 8: github issue 37: header bug\nhttps://github.com/chaoslawful/lua-nginx-module/issues/37\n\nhttps://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2\n   Just as in HTTP/1.x, header field names are strings of ASCII\n   characters that are compared in a case-insensitive fashion. However,\n   header field names MUST be converted to lowercase prior to their\n   encoding in HTTP/2.  A request or response containing uppercase\n   header field names MUST be treated as malformed\n--- no_http2\n--- config\n    location /sub {\n        content_by_lua '\n            ngx.header[\"Set-Cookie\"] = {\"TestCookie1=foo\", \"TestCookie2=bar\"};\n            ngx.say(\"Hello\")\n        ';\n    }\n    location /lua {\n        content_by_lua '\n            -- local yajl = require \"yajl\"\n            ngx.header[\"Set-Cookie\"] = {}\n            local res = ngx.location.capture(\"/sub\")\n\n            for i,j in pairs(res.header) do\n                ngx.header[i] = j\n            end\n\n            -- ngx.say(\"set-cookie: \", yajl.to_string(res.header[\"Set-Cookie\"]))\n\n            ngx.send_headers()\n            ngx.print(\"body: \", res.body)\n        ';\n    }\n--- request\nGET /lua\n--- raw_response_headers_like eval\nmy $headers;\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $headers = \".*set-cookie: TestCookie1=foo\\r\nset-cookie: TestCookie2=bar.*\"\n} else {\n    $headers = \".*Set-Cookie: TestCookie1=foo\\r\nSet-Cookie: TestCookie2=bar.*\"\n}\n\n$headers;\n\n\n\n=== TEST 9: memory leak\n--- config\n    location /foo {\n        content_by_lua_file 'html/foo.lua';\n    }\n--- user_files\n>>> foo.lua\nlocal res = {}\nres = {'good 1', 'good 2', 'good 3'}\nreturn ngx.redirect(\"/somedir/\" .. ngx.escape_uri(res[math.random(1,#res)]))\n--- request\n    GET /foo\n--- response_body\n--- SKIP\n\n\n\n=== TEST 10: capturing locations with internal redirects (no lua redirect)\n--- config\n    location /bar {\n        echo Bar;\n    }\n    location /foo {\n        #content_by_lua '\n        #ngx.exec(\"/bar\")\n        #';\n        echo_exec /bar;\n    }\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\")\n            ngx.print(res.body)\n        ';\n    }\n--- request\n    GET /main\n--- response_body\nBar\n\n\n\n=== TEST 11: capturing locations with internal redirects (lua redirect)\n--- config\n    location /bar {\n        content_by_lua 'ngx.say(\"Bar\")';\n    }\n    location /foo {\n        content_by_lua '\n            ngx.exec(\"/bar\")\n        ';\n    }\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\")\n            ngx.print(res.body)\n        ';\n    }\n--- request\n    GET /main\n--- response_body\nBar\n\n\n\n=== TEST 12: capturing locations with internal redirects (simple index)\n--- config\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/\")\n            ngx.print(res.body)\n        ';\n    }\n--- request\n    GET /main\n--- response_body chop\n<html><head><title>It works!</title></head><body>It works!</body></html>\n\n\n\n=== TEST 13: capturing locations with internal redirects (more lua statements)\n--- config\n    location /bar {\n        content_by_lua '\n            ngx.say(\"hello\")\n            ngx.say(\"world\")\n        ';\n    }\n    location /foo {\n        #content_by_lua '\n        #ngx.exec(\"/bar\")\n        #';\n        echo_exec /bar;\n    }\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\")\n            ngx.print(res.body)\n        ';\n    }\n--- request\n    GET /main\n--- response_body\nhello\nworld\n\n\n\n=== TEST 14: capturing locations with internal redirects (post subrequest with internal redirect)\n--- config\n    location /bar {\n        lua_need_request_body on;\n        client_body_in_single_buffer on;\n\n        content_by_lua '\n            ngx.say(ngx.var.request_body)\n        ';\n    }\n    location /foo {\n        #content_by_lua '\n        #ngx.exec(\"/bar\")\n        #';\n        echo_exec /bar;\n    }\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\", { method = ngx.HTTP_POST, body = \"hello\" })\n            ngx.print(res.body)\n        ';\n    }\n--- request\n    GET /main\n--- response_body\nhello\n\n\n\n=== TEST 15: nginx rewrite works in subrequests\n--- config\n    rewrite /foo /foo/ permanent;\n    location = /foo/ {\n        echo hello;\n    }\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\")\n            ngx.say(\"status = \", res.status)\n            ngx.say(\"Location: \", res.header[\"Location\"] or \"nil\")\n        ';\n    }\n--- request\n    GET /main\n--- response_body\nstatus = 301\nLocation: /foo/\n\n\n\n=== TEST 16: nginx rewrite works in subrequests\n--- config\n    access_by_lua '\n        local res = ngx.location.capture(ngx.var.uri)\n        ngx.say(\"status = \", res.status)\n        ngx.say(\"Location: \", res.header[\"Location\"] or \"nil\")\n        ngx.exit(200)\n    ';\n--- request\n    GET /foo\n--- user_files\n>>> foo/index.html\nIt works!\n--- response_body\nstatus = 301\nLocation: /foo/\n--- no_check_leak\n\n\n\n=== TEST 17: set content-type header with charset\n--- config\n    location /lua {\n        charset GBK;\n        content_by_lua '\n            ngx.header.content_type = \"text/xml; charset=UTF-8\"\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\n    GET /lua\n--- response_body\nhi\n--- response_headers\nContent-Type: text/xml; charset=UTF-8\n\n\n\n=== TEST 18: set response header content-type with charset\n--- config\n    location /lua {\n        charset GBK;\n        content_by_lua '\n            ngx.header.content_type = \"text/xml\"\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\n    GET /lua\n--- response_body\nhi\n--- response_headers\nContent-Type: text/xml; charset=GBK\n\n\n\n=== TEST 19: get by-position capturing variables\n--- config\n    location ~ '^/lua/(.*)' {\n        content_by_lua '\n            ngx.say(ngx.var[1] or \"nil\")\n        ';\n    }\n--- request\n    GET /lua/hello\n--- response_body\nhello\n\n\n\n=== TEST 20: get by-position capturing variables ($0)\n--- config\n    location ~ '^/lua/(.*)' {\n        content_by_lua '\n            ngx.say(ngx.var[0] or \"nil\")\n        ';\n    }\n--- request\n    GET /lua/hello\n--- response_body\nnil\n\n\n\n=== TEST 21: get by-position capturing variables (exceeding captures)\n--- config\n    location ~ '^/lua/(.*)' {\n        content_by_lua '\n            ngx.say(ngx.var[2] or \"nil\")\n        ';\n    }\n--- request\n    GET /lua/hello\n--- response_body\nnil\n\n\n\n=== TEST 22: get by-position capturing variables ($1, $2)\n--- config\n    location ~ '^/lua/(.*)/(.*)' {\n        content_by_lua '\n            ngx.say(ngx.var[-1] or \"nil\")\n            ngx.say(ngx.var[0] or \"nil\")\n            ngx.say(ngx.var[1] or \"nil\")\n            ngx.say(ngx.var[2] or \"nil\")\n            ngx.say(ngx.var[3] or \"nil\")\n            ngx.say(ngx.var[4] or \"nil\")\n        ';\n    }\n--- request\n    GET /lua/hello/world\n--- response_body\nnil\nnil\nhello\nworld\nnil\nnil\n\n\n\n=== TEST 23: set special variables\n--- config\n    location /main {\n        #set_unescape_uri $cookie_a \"hello\";\n        set $http_a \"hello\";\n        content_by_lua '\n            ngx.say(ngx.var.http_a)\n        ';\n    }\n--- request\n    GET /main\n--- response_body\nhello\n--- SKIP\n\n\n\n=== TEST 24: set special variables\n--- config\n    location /main {\n        content_by_lua '\n            dofile(ngx.var.realpath_root .. \"/a.lua\")\n        ';\n    }\n    location /echo {\n        echo hi;\n    }\n--- request\n    GET /main\n--- user_files\n>>> a.lua\nngx.location.capture(\"/echo\")\n--- response_body\n--- SKIP\n\n\n\n=== TEST 25: set 20+ headers\n--- config\n    location /test {\n        rewrite_by_lua '\n            ngx.req.clear_header(\"Authorization\")\n        ';\n        echo $http_a1;\n        echo $http_authorization;\n        echo $http_a2;\n        echo $http_a3;\n        echo $http_a23;\n        echo $http_a24;\n        echo $http_a25;\n    }\n--- request\n    GET /test\n--- more_headers eval\nmy $i = 1;\nmy $s;\nwhile ($i <= 25) {\n    $s .= \"A$i: $i\\n\";\n    if ($i == 22) {\n        $s .= \"Authorization: blah\\n\";\n    }\n    $i++;\n}\n#warn $s;\n$s\n--- response_body\n1\n\n2\n3\n23\n24\n25\n\n\n\n=== TEST 26: globals sharing by using _G\n--- config\n    location /test {\n        content_by_lua '\n            if _G.t then\n                _G.t = _G.t + 1\n            else\n                _G.t = 0\n            end\n            ngx.print(t)\n        ';\n    }\n--- pipelined_requests eval\n[\"GET /test\", \"GET /test\", \"GET /test\"]\n--- response_body_like eval\n[qr/\\A[036]\\z/, qr/\\A[147]\\z/, qr/\\A[258]\\z/]\n\n\n\n=== TEST 27: globals sharing by using _G (set_by_lua*)\n--- config\n    location /test {\n        set_by_lua $a '\n            if _G.t then\n                _G.t = _G.t + 1\n            else\n                _G.t = 0\n            end\n            return t\n        ';\n        echo -n $a;\n    }\n--- pipelined_requests eval\n[\"GET /test\", \"GET /test\", \"GET /test\"]\n--- response_body_like eval\n[qr/\\A[036]\\z/, qr/\\A[147]\\z/, qr/\\A[258]\\z/]\n\n\n\n=== TEST 28: globals sharing by using _G (log_by_lua*)\n--- http_config\n    lua_shared_dict log_dict 100k;\n--- config\n    location /test {\n        content_by_lua '\n            local log_dict = ngx.shared.log_dict\n            ngx.print(log_dict:get(\"cnt\") or 0)\n        ';\n\n        log_by_lua '\n            local log_dict = ngx.shared.log_dict\n            if _G.t then\n                _G.t = _G.t + 1\n            else\n                _G.t = 1\n            end\n            log_dict:set(\"cnt\", t)\n        ';\n    }\n--- pipelined_requests eval\n[\"GET /test\", \"GET /test\", \"GET /test\"]\n--- response_body_like eval\n[qr/\\A[036]\\z/, qr/\\A[147]\\z/, qr/\\A[258]\\z/]\n\n\n\n=== TEST 29: globals sharing by using _G (header_filter_by_lua*)\n--- config\n    location /test {\n        header_filter_by_lua '\n            if _G.t then\n                _G.t = _G.t + 1\n            else\n                _G.t = 0\n            end\n            ngx.ctx.cnt = tostring(t)\n        ';\n        content_by_lua '\n            ngx.send_headers()\n            ngx.print(ngx.ctx.cnt or 0)\n        ';\n    }\n--- pipelined_requests eval\n[\"GET /test\", \"GET /test\", \"GET /test\"]\n--- response_body_like eval\n[qr/\\A[036]\\z/, qr/\\A[147]\\z/, qr/\\A[258]\\z/]\n\n\n\n=== TEST 30: globals sharing by using _G (body_filter_by_lua*)\n--- config\n    location /test {\n        body_filter_by_lua '\n            if _G.t then\n                _G.t = _G.t + 1\n            else\n                _G.t = 0\n            end\n            ngx.ctx.cnt = _G.t\n        ';\n        content_by_lua '\n            ngx.print(\"a\")\n            ngx.say(ngx.ctx.cnt or 0)\n        ';\n    }\n--- request\nGET /test\n--- response_body_like eval\nqr/\\Aa[036]\n\\z/\n--- no_error_log\n[error]\n\n\n\n=== TEST 31: set content-type header with charset and default_type\n--- http_config\n--- config\n    location /lua {\n        default_type application/json;\n        charset utf-8;\n        charset_types application/json;\n        content_by_lua 'ngx.say(\"hi\")';\n    }\n--- request\n    GET /lua\n--- response_body\nhi\n--- response_headers\nContent-Type: application/json; charset=utf-8\n\n\n\n=== TEST 32: hang on upstream_next (from kindy)\n--- no_http2\n--- no_check_leak\n--- http_config\n    upstream xx {\n        server 127.0.0.1:$TEST_NGINX_SERVER_PORT max_fails=5;\n        server 127.0.0.1:$TEST_NGINX_SERVER_PORT max_fails=5;\n    }\n\n    server {\n        server_name \"xx\";\n        listen $TEST_NGINX_SERVER_PORT;\n\n        return 444;\n    }\n--- config\n    location = /t {\n        proxy_next_upstream off;\n        proxy_pass http://xx;\n    }\n--- request\n    GET /t\n--- timeout: 1\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- error_log\nupstream prematurely closed connection while reading response header from upstream\n\n\n\n=== TEST 33: last_in_chain is set properly in subrequests\n--- config\n    location = /sub {\n        echo hello;\n        body_filter_by_lua '\n            local eof = ngx.arg[2]\n            if eof then\n                print(\"eof found in body stream\")\n            end\n        ';\n    }\n\n    location = /main {\n        echo_location /sub;\n    }\n\n--- request\n    GET /main\n--- response_body\nhello\n--- log_level: notice\n--- error_log\neof found in body stream\n\n\n\n=== TEST 34: testing a segfault when using ngx_poll_module + ngx_resolver\nSee more details here: http://mailman.nginx.org/pipermail/nginx-devel/2013-January/003275.html\n\nhttp3 may cache the dns result.\nso need to skip for http3\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3}\n--- config\n    location /t {\n        set $myserver nginx.org;\n        proxy_pass http://$myserver/;\n        resolver 127.0.0.1:6789;\n    }\n--- request\n    GET /t\n--- ignore_response\n--- abort\n--- timeout: 0.3\n--- log_level: notice\n--- no_error_log\n[alert]\n--- error_log eval\nqr/(?:send|recv)\\(\\) failed \\(\\d+: Connection refused\\) while resolving/\n--- curl_error eval\nqr/curl: \\(28\\) Operation timed out after \\d+ milliseconds with 0 bytes received/\n\n\n\n=== TEST 35: github issue #218: ngx.location.capture hangs when querying a remote host that does not exist or is really slow to respond\n--- config\n    set $myurl \"https://not-exist.agentzh.org\";\n    location /toto {\n        content_by_lua '\n                local proxyUrl = \"/myproxy/entity\"\n                local res = ngx.location.capture( proxyUrl,  { method = ngx.HTTP_GET })\n                ngx.say(\"Hello, \", res.status)\n            ';\n    }\n    location ~ /myproxy {\n\n        rewrite    ^/myproxy/(.*)  /$1  break;\n        resolver_timeout 3s;\n        #resolver 172.16.0.23; #  AWS DNS resolver address is the same in all regions - 172.16.0.23\n        resolver $TEST_NGINX_RESOLVER;\n        proxy_read_timeout 1s;\n        proxy_send_timeout 1s;\n        proxy_connect_timeout 1s;\n        proxy_pass $myurl:443;\n        proxy_pass_request_body off;\n        proxy_set_header Content-Length 0;\n        proxy_set_header  Accept-Encoding  \"\";\n    }\n\n--- request\nGET /toto\n\n--- stap2\nF(ngx_http_lua_post_subrequest) {\n    println(\"lua post subrequest\")\n    print_ubacktrace()\n}\n\n--- response_body\nHello, 502\n\n--- error_log\nnot-exist.agentzh.org could not be resolved\n--- timeout: 10\n\n\n\n=== TEST 36: line comments in the last line of the inlined Lua code\n--- config\n    location /lua {\n        content_by_lua 'ngx.say(\"ok\") -- blah';\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- no_error_log\n[error]\n\n\n\n=== TEST 37: resolving names with a trailing dot\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        resolver $TEST_NGINX_RESOLVER ipv6=off;\n        set $myhost 'agentzh.org.';\n        proxy_pass http://$myhost/misc/.vimrc;\n    }\n--- request\nGET /t\n--- response_body_like: An example for a vimrc file\n--- no_error_log\n[error]\n--- timeout: 10\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 38: resolving names with a trailing dot\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n    server {\n        listen \\$TEST_NGINX_RAND_PORT_1;\n\n        location = /t {\n            echo 'args: \\$args';\n        }\n    }\n\"\n--- config\n    location = /t {\n        set $args \"foo=1&bar=2\";\n        proxy_pass http://127.0.0.1:$TEST_NGINX_RAND_PORT_1;\n    }\n\n--- request\nGET /t\n--- response_body\nargs: foo=1&bar=2\n--- no_error_log\n[error]\n--- no_check_leak\n\n\n\n=== TEST 39: lua_code_cache off + setkeepalive\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    lua_code_cache off;\n    location = /t {\n        set $port $TEST_NGINX_REDIS_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go(port)\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local sock2 = ngx.socket.tcp()\n\n    sock:settimeout(1000)\n    sock2:settimeout(6000000)\n\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    local ok, err = sock2:connect(\"127.0.0.1\", port)\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    local ok, err = sock:setkeepalive(100, 100)\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\n\n    local ok, err = sock2:setkeepalive(200, 100)\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\n\n    ngx.say(\"done\")\nend\n--- request\nGET /t\n--- stap2\nF(ngx_close_connection) {\n    println(\"=== close connection\")\n    print_ubacktrace()\n}\n--- stap_out2\n--- response_body\ndone\n--- wait: 0.5\n--- no_error_log\n[error]\n\n\n\n=== TEST 40: .lua file of exactly N*1024 bytes (github issue #385)\n--- config\n    location = /t {\n        content_by_lua_file html/a.lua;\n    }\n\n--- user_files eval\nmy $s = \"ngx.say('ok')\\n\";\n\">>> a.lua\\n\" . (\" \" x (8192 - length($s))) . $s;\n\n--- request\nGET /t\n--- response_body\nok\n--- no_error_log\n[error]\n\n\n\n=== TEST 41: https proxy has no timeout protection for ssl handshake\n--- http_config\n    # to suppress a valgrind false positive in the nginx core:\n    proxy_ssl_session_reuse off;\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        ssl_certificate ../html/test.crt;\n        ssl_certificate_key ../html/test.key;\n\n        location /foo {\n            echo foo;\n        }\n    }\n\n    upstream local {\n        server unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n    }\n\n--- config\n    location = /t {\n        proxy_pass https://local/foo;\n    }\n\n--- user_files eval\n\">>> test.key\n$::TestCertificateKey\n>>> test.crt\n$::TestCertificate\"\n\n--- request\nGET /t\n\n--- stap\nprobe process(\"nginx\").function(\"ngx_http_upstream_ssl_handshake\") {\n    printf(\"read timer set: %d\\n\", $c->read->timer_set)\n    printf(\"write timer set: %d\\n\", $c->write->timer_set)\n}\n--- stap_out\nread timer set: 0\nwrite timer set: 1\n\n--- response_body eval\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 42: tcp: nginx crash when resolve an not exist domain in ngx.thread.spawn\nhttps://github.com/openresty/lua-nginx-module/issues/1915\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location = /t {\n        content_by_lua_block {\n            local function tcp(host, port)\n                local sock = ngx.socket.tcp()\n                local ok,err = sock:connect(host, port)\n                if not ok then\n                    ngx.log(ngx.WARN, \"failed: \", err)\n                    sock:close()\n                    return false\n                end\n\n                sock:close()\n                return true\n            end\n\n            local host = \"nonexistent.openresty.org\"\n            local port = 80\n\n            local threads = {}\n            for i = 1, 3 do\n                threads[i] = ngx.thread.spawn(tcp, host, port)\n            end\n\n            local ok, res = ngx.thread.wait(threads[1],threads[2],threads[3])\n            if not ok then\n                ngx.say(\"failed to wait thread\")\n                return\n            end\n\n            ngx.say(\"res: \", res)\n\n            for i = 1, 3 do\n                ngx.thread.kill(threads[i])\n            end\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nres: false\n--- error_log\nnonexistent.openresty.org could not be resolved\n\n\n\n=== TEST 43: domain exists with tcp socket\nhttps://github.com/openresty/lua-nginx-module/issues/1915\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location = /t {\n        content_by_lua_block {\n            local function tcp(host, port)\n                local sock = ngx.socket.tcp()\n                local ok,err = sock:connect(host, port)\n                if not ok then\n                    ngx.log(ngx.WARN, \"failed: \", err)\n                    sock:close()\n                    return false\n                end\n\n                sock:close()\n                return true\n            end\n\n            local host = \"www.openresty.org\"\n            local port = 80\n\n            local threads = {}\n            for i = 1, 3 do\n                threads[i] = ngx.thread.spawn(tcp, host, port)\n            end\n\n            local ok, res = ngx.thread.wait(threads[1],threads[2],threads[3])\n            if not ok then\n                ngx.say(\"failed to wait thread\")\n                return\n            end\n\n            ngx.say(\"res: \", res)\n\n            for i = 1, 3 do\n                ngx.thread.kill(threads[i])\n            end\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nres: true\n\n\n\n=== TEST 44: domain exists with udp socket\nhttps://github.com/openresty/lua-nginx-module/issues/1915\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location = /t {\n        content_by_lua_block {\n            local function udp(host, port)\n                local sock = ngx.socket.udp()\n                local ok,err = sock:setpeername(host, port)\n                if not ok then\n                    ngx.log(ngx.WARN, \"failed: \", err)\n                    sock:close()\n                    return false\n                end\n\n                sock:close()\n                return true\n            end\n\n            local host = \"nonexistent.openresty.org\"\n            local port = 80\n\n            local threads = {}\n            for i = 1, 3 do\n                threads[i] = ngx.thread.spawn(udp, host, port)\n            end\n\n            local ok, res = ngx.thread.wait(threads[1],threads[2],threads[3])\n            if not ok then\n                ngx.say(\"failed to wait thread\")\n                return\n            end\n\n            ngx.say(\"res: \", res)\n\n            for i = 1, 3 do\n                ngx.thread.kill(threads[i])\n            end\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nres: false\n--- error_log\nnonexistent.openresty.org could not be resolved\n\n\n\n=== TEST 45: udp: nginx crash when resolve an not exist domain in ngx.thread.spawn\nhttps://github.com/openresty/lua-nginx-module/issues/1915\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location = /t {\n        content_by_lua_block {\n            local function udp(host, port)\n                local sock = ngx.socket.udp()\n                local ok,err = sock:setpeername(host, port)\n                if not ok then\n                    ngx.log(ngx.WARN, \"failed: \", err)\n                    sock:close()\n                    return false\n                end\n\n                sock:close()\n                return true\n            end\n\n            local host = \"www.openresty.org\"\n            local port = 80\n\n            local threads = {}\n            for i = 1, 3 do\n                threads[i] = ngx.thread.spawn(udp, host, port)\n            end\n\n            local ok, res = ngx.thread.wait(threads[1],threads[2],threads[3])\n            if not ok then\n                ngx.say(\"failed to wait thread\")\n                return\n            end\n\n            ngx.say(\"res: \", res)\n\n            for i = 1, 3 do\n                ngx.thread.kill(threads[i])\n            end\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nres: true\n\n\n\n=== TEST 46: nginx crash when parsing a word or a single configuration item that is too long\nhttps://github.com/openresty/lua-nginx-module/issues/1938\n--- http_config\n    init_worker_by_lua '\n        err_big_str = 'A NA<document><ghjnxnpnaryyhzyfehuyjxzoilebgazuifhn foo=bar><other_tag foo=bar><ahziqttu foo=bar><a foo=bar><other_tag foo=bar><other_tag foo=bar><other_tag foo=bar><nzzpftierhdtdeippzlyjrmkbtljunmkxhohxmbdmgeeazpb foo=bar></nzzpftierhdtdeippzlyjrmkbtljunmkxhohxmbdmgeeazpb><qai foo=bar></qai></other_tag></other_tag><other_tag foo=bar></other_tag><other_tag foo=bar></other_tag></other_tag><some_tag foo=bar></some_tag><some_tag foo=bar><mdbrjkon foo=bar><other_tag foo=bar></other_tag></mdbrjkon><mttiqvw foo=bar></mttiqvw></some_tag><some_tag foo=bar></some_tag></a><lae foo=bar></lae><ds foo=bar></ds><some_tag foo=bar><other_tag foo=bar></other_tag></some_tag><other_tag foo=bar></other_tag></ahziqttu></other_tag><a foo=bar><some_tag foo=bar></some_tag><some_tag foo=bar><other_tag foo=bar></other_tag></some_tag></a><other_tag foo=bar><cxfpg foo=bar></cxfpg><some_tag foo=bar></some_tag></other_tag></ghjnxnpnaryyhzyfehuyjxzoilebgazuifhn><some_tag foo=bar><other_tag foo=bar><other_tag foo=bar><some_tag foo=bar><some_tag foo=bar></some_tag><other_tag foo=bar></other_tag></some_tag><some_tag foo=bar></some_tag><some_tag foo=bar><a foo=bar></a></some_tag><a foo=bar></a></other_tag><a foo=bar></a></other_tag><a foo=bar><wblh foo=bar><jyfzglfbaxfjvhtaiysmsexwusvrvzu foo=bar><other_tag foo=bar></other_tag></jyfzglfbaxfjvrtaiysmsexwusvrvzu><a foo=bar><other_tag foo=bar></other_tag></a></wblh><ycnivdryxanudpgzmgugzyjrnacandijqitfosjrxjuosiwhxxgwgqpwzjcyelstgzveugtmjilnkydyktoqywjyydtcgtabowmbxnjpttkxqjpazdsgzeutjfzgvafnovu@zgccxvypzbkbbsizllwitznecdbyiynopkzsyazlhyslqlwkqqnzuvvdlavwvspwzpivmmreycogbinpvhvfscjmwwwllppjholetfvcbezdwrfczqbdrogr foo=bar></ycnivdryxanudpgzmgugzyjrnacandijqitfosjrxjuosiwhxxgwgqpwzjcyelstgzveugtmjilnkydyktoqywjyydtcgtabowmbxnjpttkxqjpazdsgzeutjfzgvafnovumzgccxvypzbkbbsizllwitznecdbyiynopkzsyazlhyslqlwkqqnzuvvdlavwvspwzpivmmreycogbinpvhvfscjmwwwllppjholetfvcbezdwrfczqbdrogr></a><s foo=bar></s><some_tag foo=bar></some_tag><some_tag foo=bar></some_tag></some_tag><oin foo=bar><other_tag foo=bar><other_tag foo=bar></other_tag></other_tag><other_tag foo=bar></other_tag><other_tag foo=bar></other_tag></oin><other_tag foo=bar><other_tag foo=bar><some_tag foo=bar><other_tag foo=bar></other_tag></some_tag><other_tag foo=bar><some_tag foo=bar><some_tag foo=bar><some_tag foo=bar><other_tag foo=bar></other_tag></some_tag><xg foo=bar></xg></some_tag><ibsolavsdhkcovsbqddq foo=bar><bjodqvqtcgizzbefemdqiljssgxibmprzhxifaciftbl foo=bar></bjodqvqtcgizzbefemdqiljssgxibmprzhxifaciftbl></ibsolavsdhkcovsbqddq><s foo=bar><j foo=bar><other_tag foo=bar></other_tag></j></s><other_tag foo=bar><zte foo=bar></zte><other_tag foo=bar><a foo=bar></a></other_tag></other_tag></some_tag><some_tag foo=bar><other_tag foo=bar></other_tag></some_tag></other_tag><other_tag foo=bar><some_tag foo=bar><other_tag foo=bar></other_tag><other_tag foo=bar><other_tag foo=bar><some_tag foo=bar></some_tag></other_tag></other_tag><some_tag foo=bar></some_tag></some_tag><other_tag foo=bar></other_tag></other_tag><some_tag foo=bar></some_tag></other_tag><ynorkudnfqlyozuf foo=bar><some_tag foo=bar><some_tag foo=bar></some_tag></some_tag><some_tag foo=bar><a foo=bar></a></some_tag><some_tag foo=bar><some_tag foo=bar></some_tag></some_tag><other_tag foo=bar><some_tag foo=bar><gywpe foo=bar></gywpe></some_tag><some_tag foo=bar></some_tag><some_tag foo=bar></some_tag></other_tag><some_tag foo=bar><ycbfctvudqzhnasdtgwsylenjzo foo=bar></ycbfctvudqzhnasdtgwsylenjzo></some_tag></ynorkudnfqlyozuf><some_tag foo=bar></some_tag><other_tag foo=bar></other_tag><bpxlcvo foo=bar></bpxlcvo></other_tag><other_tag foo=bar><some_tag foo=bar><some_tag foo=bar><bsgabtkeonafnvroqlmlprxxhlkayhlmxmanhomgrweqevvqowuvnrvfazckbpxihviccqvfeciafjuxpiukkyfmirugowshqyxuvkzxjwfyl foo=bar><bujx foo=bar><other_tag foo=bar></other_tag></bujx></bsgabtkeonafnvroqlmlprxxhlkayhlmxmanhomgrweqevvqowuvnrvfazckbpxihviccqvfeciafjuxpiukkyfmirugowshqyxuvkzxjwfyl></some_tag><some_tag foo=bar></some_tag><other_tag foo=bar><other_tag foo=bar></other_tag></other_tag></some_tag><other_tag foo=bar></other_tag><yn foo=bar></yn><some_tag foo=bar></some_tag></other_tag><some_tag foo=bar><some_tag foo=bar><yjfgivoaqys foo=bar><some_tag foo=bar></some_tag></yjfgivoaqys><some_tag foo=bar></some_tag></some_tag><some_tag foo=bar><some_tag foo=bar></some_tag></some_tag><other_tag foo=bar><some_tag foo=bar><other_tag foo=bar></other_tag></some_tag></other_tag><some_tag foo=bar><other_tag foo=bar><q foo=bar></q></other_tag><some_tag foo=bar><some_tag foo=bar><some_tag foo=bar><fimlcfqpgrfgmqlvy foo=bar><some_tag foo=bar><other_tag foo=bar><other_tag foo=bar><other_tag foo=bar></other_tag></other_tag><ozbxovtd foo=bar></ozbxovtd></other_tag><a foo=bar><vhilkxdosukumkwuryepsspwraoqcetjpnmplka foo=bar></vhilkxdosukumkwuryepsspwraoqcetjpnmplka><other_tag foo=bar></other_tag></a><other_tag foo=bar><a foo=bar></a></other_tag><some_tag foo=bar></some_tag></some_tag><other_tag foo=bar><other_tag foo=bar></other_tag></other_tag></fimlcfqpgrfgmqlvy></some_tag><some_tag foo=bar><some_tag foo=bar><eslmjazk foo=bar></eslmjazk><some_tag foo=bar><some_tag foo=bar></some_tag><i foo=bar></i><some_tag foo=bar></some_tag><tpwkibjgpffwateypjezqgaomneab foo=bar></tpwkibjgpffwateypjezqgaomneab></some_tag><a foo=bar></a><okpozscqucclyrbjantdwptdyxhqhfitkjmeduuagzhfontbgjkwbaocccreequtrdwoatikmalucrlffnustjdgeaskfekewxpwtgmgtmdhhbyvgafbyjfjtlwmiyfoetprbfmpasmdobxylzshferaxicajxawnxdxkpszeqeyqziglwbczzhbhzkpphemgqghwfbrlqhczjffzefstydpnufvoknbpvszxfrqtqhuybtayd foo=bar></okpozscqucclyrbjantdwptdyxhqhfitkjmeduuagzhfontbgjkwbaocccreequtrdwoatikmalucrlffnustjdgeaskfekewxpwtgmgtmdhhbyvgafbyjfjtlwmiyfoetprbfmpasmdobxylzshferaxicajxawnxdxkpszeqeyqziglwbczzhbhzkpphemgqghwfbrlqhczjffzefstydpnufvoknbpvszxfrqtqhuybtayd></some_tag><some_tag foo=bar></some_tag></some_tag><a foo=bar><other_tag foo=bar></other_tag></a><some_tag foo=bar></some_tag><other_tag foo=bar></other_tag></some_tag><pu foo=bar><a foo=bar><some_tag foo=bar></some_tag><some_tag foo=bar><dswgxeosxelilaawqnqeqdagheheqomtuisiwcneaoetifviqqgtkawqapggjmoadxhwxokszbrfvxzedyzeplkkceleiwkjvzzatawfaqkjuogpvocrkpzbcrqandfrxrrwkidpfoseyhjkapbnwenzvprrsmstcrwwgvzprbngzfsolnuoxltbazguzolvqkahdwqgosbrzxzaiozletuhqimihu foo=bar></dswgxeosxelilaawqnqeqdagheheqomtuisiwcneaoetifviqqgtkawqapggjmoadxhwxokszbrfvxzedyzeplkkceleiwkjvzzatawfaqkjuogpvocrkpzbcrqandfrxrrwkidpfoseyhjkapbnwenzvprrsmstcrwwgvzprbngzfsolnuoxltbazguzolvqkahdwqgosbrzxzaiozletuhqimihu></some_tag><qozyyy foo=bar></qozyyy></a><other_tag foo=bar><other_tag foo=bar></other_tag></other_tag><a foo=bar><some_tag foo=bar></some_tag></a><auwvp foo=bar><pdwznxmyechrdlyirpz foo=bar><some_tag foo=bar></some_tag></pdwznxmyechrdlyirpz><some_tag foo=bar><hetkrhunm foo=bar></hetkrhunm><ivaxkibutldrsmqncviihdarsmhezhijyculvmkefbsnxfbxdfzizxkediuvjpplcyhallsjvnrxjkmrjinexelrqirrixajcpqsdtdkvajlktotwzxawuterepyyvtoywpcbiwihdkrirrgbbwguqrgcybhxxyraobyyui foo=bar></ivaxkibutldrsmqncviihdarsmhezhijyculvmkefbsnxfbxdfzizxkediuvjpplcyhallsjvnrxjkmrjinexelrqirrixajcpqsdtdkvajlktotwzxawuterepyyvtoywpcbiwihdkrirrgbbwguqrgcybhxxyraobyyui></some_tag></auwvp><other_tag foo=bar></other_tag><other_tag foo=bar></other_tag></pu><tjntyubedfylkigrecanowgsmvxguybllkyrdfntpodukwzojuztpwmqijrltm foo=bar></tjntyubedfylkigrecanowgsmvxguybllkyrdfntpodukwzojuztpwmqijrltm></some_tag><ztnairlelhvuujacjepxegwehtrfkawgggwbanfwheyjdmqlxicwvbtel foo=bar></ztnairlelhvuujacjepxegwehtrfkawgggwbanfwheyjdmqlxicwvbtel><othe>'\n    ';\n--- config\n    location /t {\n        content_by_lua '\n            ngx.say(\"hello world\")\n        ';\n    }\n--- request\nGET /t\n--- response_body\nres: true\n--- no_error_log\n[error]\n--- must_die\n--- error_log eval\nqr/\\[emerg\\] \\d+#\\d+: unexpected \"A\" in/\n\n\n\n=== TEST 47: cosocket does not exit on worker_shutdown_timeout\nThis test must enable master process\n--- SKIP\n--- main_config\nworker_shutdown_timeout 1;\n--- config\nlocation /t {\n    content_by_lua_block {\n        local function thread_func(port)\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            local bytes, err = sock:send(\"hello\")\n            if bytes ~= 5 then\n                sock:close()\n                return ngx.exit(500)\n            end\n\n            local data, err = sock:receive(20)\n            local line, err, partial = sock:receive()\n            if not line then\n                ngx.log(ngx.ERR, \"failed to read a line: \", err)\n                return\n            end\n\n            ngx.log(ngx.ERR, \"successfully read a line: \", line)\n        end\n\n        local function timer_func(port)\n            ngx.thread.spawn(thread_func, port)\n        end\n\n        ngx.timer.at(1, timer_func, ngx.var.server_port)\n        ngx.say(\"Hello world\")\n    }\n}\n--- request\n    GET /t\n--- response_body\nHello world\n--- shutdown_error_log eval\nmy $expr;\n\nif ($ENV{TEST_NGINX_USE_HTTP3}) {\n    $expr = qr|lua close the global Lua VM|\n} else {\n    $expr = qr/failed to read a line: closed|attempt to send data on a closed socket/\n}\n\n$expr;\n--- timeout: 1.2\n\n\n\n=== TEST 48: nginx crashes when encountering an illegal http if header\ncrash with ngx.send_headers()\n--- main_config\n--- config\nerror_page 412 /my_error_handler_412;\n\nlocation /t {\n    rewrite_by_lua_block {\n        ngx.send_headers()\n        -- ngx.print() -- this also triggers the bug\n    }\n}\nlocation = /my_error_handler_412 {\n    return 412 \"hello\";\n}\n--- request\n    GET /t\n--- more_headers\nIf-Match: 1\n--- error_code: 412\n--- response_body eval\nqr/\\Ahello\\z/\n\n\n\n=== TEST 49: nginx crashes when encountering an illegal http if header\ncrash with ngx.print()\n--- main_config\n--- config\nerror_page 412 /my_error_handler_412;\n\nlocation /t {\n    rewrite_by_lua_block {\n        ngx.print()\n    }\n}\nlocation = /my_error_handler_412 {\n    return 412 \"hello\";\n}\n--- request\n    GET /t\n--- more_headers\nIf-Match: 1\n--- error_code: 412\n--- response_body eval\nqr/\\Ahello\\z/\n\n\n\n=== TEST 50: nginx crashes when encountering an illegal http if header\ncrash with ngx.print()\n--- main_config\n--- config\nerror_page 412 /my_error_handler_412;\n\nlocation /t {\n    access_by_lua_block {\n        local ngx_resp = require \"ngx.resp\"\n        ngx_resp.bypass_if_checks()\n        ngx.print(\"hello\")\n        ngx.exit(200)\n    }\n}\nlocation = /my_error_handler_412 {\n    content_by_lua_block {\n        ngx.sleep(0.002)\n        ngx.header[\"Content-Type\"] = \"text/plain\"\n    }\n}\n--- request\n    GET /t\n--- more_headers\nIf-Match: 1\n--- error_code: 200\n--- response_body eval\nqr/\\Ahello\\z/\n\n\n\n=== TEST 51: subrequest cycle problem in rewrite_by_lua_file\n--- http_config\n    lua_code_cache off;\n--- config\n    set $main \"foo\";\n    set $sub \"bar\";\n    location = /main {\n        rewrite_by_lua_file html/main.lua;\n        echo $main;\n    }\n\n    location = /sub {\n        rewrite_by_lua_file html/sub.lua;\n        echo $sub;\n    }\n--- user_files\n>>> main.lua\nlocal res = ngx.location.capture(\"/sub\")\nngx.var.main = \"main \" .. res.body\n>>> sub.lua\nngx.var.sub = \"sub\"\n\n--- pipelined_requests eval\n[\"GET /sub\", \"GET /main\"]\n--- response_body eval\n[\"sub\\n\", \"main sub\\n\\n\"]\n--- no_error_log\n[error]\n\n\n\n=== TEST 52: subrequest cycle problem in content_by_lua_file\n--- http_config\n    lua_code_cache off;\n--- config\n    location = /main {\n        content_by_lua_file html/main.lua;\n    }\n\n    location = /sub {\n        content_by_lua_file html/sub.lua;\n    }\n--- user_files\n>>> main.lua\nlocal res = ngx.location.capture(\"/sub\")\nngx.print(\"main \" .. res.body)\n>>> sub.lua\nngx.print(\"sub\")\n\n--- pipelined_requests eval\n[\"GET /sub\", \"GET /main\"]\n--- response_body eval\n[\"sub\", \"main sub\"]\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/015-status.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\n#repeat_each(120);\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 2);\n\n#no_diff();\n#no_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: no key found\n--- config\n    location /nil {\n        content_by_lua '\n            ngx.say(ngx.blah_blah == nil and \"nil\" or \"not nil\")\n        ';\n    }\n--- request\nGET /nil\n--- response_body\nnil\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: .status found\n--- config\n    location /nil {\n        content_by_lua '\n            ngx.say(ngx.status == nil and \"nil\" or \"not nil\")\n        ';\n    }\n--- request\nGET /nil\n--- response_body\nnot nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: default to 0\n--- config\n    location /nil {\n        content_by_lua '\n            ngx.say(ngx.status);\n        ';\n    }\n--- request\nGET /nil\n--- response_body\n0\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: default to 0\n--- config\n    location /nil {\n        content_by_lua '\n            ngx.say(\"blah\");\n            ngx.say(ngx.status);\n        ';\n    }\n--- request\nGET /nil\n--- response_body\nblah\n200\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: set 201\n--- config\n    location /201 {\n        content_by_lua '\n            ngx.status = 201;\n            ngx.say(\"created\");\n        ';\n    }\n--- request\nGET /201\n--- response_body\ncreated\n--- error_code: 201\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: set \"201\"\n--- config\n    location /201 {\n        content_by_lua '\n            ngx.status = \"201\";\n            ngx.say(\"created\");\n        ';\n    }\n--- request\nGET /201\n--- response_body\ncreated\n--- error_code: 201\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: set \"201.7\"\n--- config\n    location /201 {\n        content_by_lua '\n            ngx.status = \"201.7\";\n            ngx.say(\"created\");\n        ';\n    }\n--- request\nGET /201\n--- response_body\ncreated\n--- error_code: 201\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: set \"abc\"\n--- config\n    location /201 {\n        content_by_lua '\n            ngx.status = \"abc\";\n            ngx.say(\"created\");\n        ';\n    }\n--- request\nGET /201\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- no_error_log\n[crit]\n\n\n\n=== TEST 9: set blah\n--- config\n    location /201 {\n        content_by_lua '\n            ngx.blah = 201;\n            ngx.say(\"created\");\n        ';\n    }\n--- request\nGET /201\n--- response_body\ncreated\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: set ngx.status before headers are sent\n--- config\n    location /t {\n        content_by_lua '\n            ngx.say(\"ok\")\n            ngx.status = 201\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nok\n--- error_code: 200\n--- error_log eval\nqr/\\[error\\] .*? attempt to set ngx\\.status after sending out response headers/\n\n\n\n=== TEST 11: http 1.0 and ngx.status\n--- config\n    location /nil {\n        content_by_lua '\n            ngx.status = ngx.HTTP_UNAUTHORIZED\n            ngx.say(\"invalid request\")\n            ngx.exit(ngx.HTTP_OK)\n        ';\n    }\n--- request\nGET /nil HTTP/1.0\n--- response_body\ninvalid request\n--- error_code: 401\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: github issue #221: cannot modify ngx.status for responses from ngx_proxy\n--- config\n    location = /t {\n        proxy_pass http://127.0.0.1:$server_port/;\n        header_filter_by_lua '\n            if ngx.status == 206 then\n                ngx.status = ngx.HTTP_OK\n            end\n        ';\n    }\n\n--- request\nGET /t\n\n--- more_headers\nRange: bytes=0-4\n\n--- response_body chop\n<html\n\n--- error_code: 200\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: 101 response has a complete status line\n--- config\n    location /t {\n        content_by_lua '\n            ngx.status = 101\n            ngx.send_headers()\n        ';\n    }\n--- request\nGET /t\n--- raw_response_headers_like: ^HTTP/1.1 101 Switching Protocols\\r\\n\n--- error_code: 101\n--- no_error_log\n[error]\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n--- no_http2\n\n\n\n=== TEST 14: reading error status code\n--- config\n    location = /t {\n        content_by_lua 'ngx.say(\"status = \", ngx.status)';\n    }\n--- raw_request eval\n\"GET /t\\r\\n\"\n--- http09\n--- response_body\nstatus = 9\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: err status\n--- config\n    location /nil {\n        content_by_lua '\n            ngx.exit(502)\n        ';\n        body_filter_by_lua '\n            if ngx.arg[2] then\n                ngx.log(ngx.WARN, \"ngx.status = \", ngx.status)\n            end\n        ';\n    }\n--- request\nGET /nil\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- error_log\nngx.status = 502\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: ngx.status assignment should clear r->err_status\n--- config\nlocation = /t {\n    return 502;\n    header_filter_by_lua_block {\n        if ngx.status == 502 then\n            ngx.status = 654\n            ngx.log(ngx.WARN, \"ngx.status: \", ngx.status)\n        end\n    }\n}\n--- request\nGET /t\n--- response_body_like: Bad Gateway\n--- error_log\nngx.status: 654\n--- no_error_log\n[error]\n--- error_code: 654\n\n\n\n=== TEST 17: set status and reason\n--- config\nlocation = /upstream {\n    content_by_lua_block {\n        local resp = require \"ngx.resp\"\n        resp.set_status(500, \"user defined reason\")\n        ngx.say(\"set_status_and_reason\")\n    }\n}\n\nlocation /t {\n   content_by_lua_block {\n       local sock = ngx.socket.tcp()\n       local port = ngx.var.server_port\n       local ok, err = sock:connect(\"127.0.0.1\", port)\n       if not ok then\n           ngx.say(\"failed to connect: \", err)\n           return\n       end\n\n       local req = \"GET /upstream HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n       local bytes, err = sock:send(req)\n       if not bytes then\n           ngx.say(\"failed to send request: \", err)\n           return\n       end\n\n       local found = false\n       while true do\n           local line, err, part = sock:receive()\n           if line then\n               if ngx.re.find(line, \"HTTP/1.1 500 user defined reason\") then\n                   ngx.say(\"match\")\n               end\n           else\n               break\n           end\n       end\n\n       sock:close()\n   }\n}\n--- request\nGET /t\n--- response_body\nmatch\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: set ngx.status in server_rewrite_by_lua_block\ndon't proxy_pass to upstream\n--- config\n    server_rewrite_by_lua_block {\n        if ngx.var.uri == \"/t\" then\n            ngx.status = 403\n            ngx.say(\"Hello World\")\n        end\n    }\n\n    location /t {\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u;\n    }\n\n    location /u {\n        content_by_lua_block {\n            ngx.say(\"From upstream\")\n        }\n    }\n--- request\nGET /t HTTP/1.0\n--- response_body\nHello World\n--- error_code: 403\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: set ngx.status in rewrite_by_lua_block\ndon't proxy_pass to upstream\n--- config\n    location /t {\n        rewrite_by_lua_block {\n            ngx.status = 403\n            ngx.say(\"Hello World\")\n        }\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u;\n    }\n\n    location /u {\n        content_by_lua_block {\n            ngx.say(\"From upstream\")\n        }\n    }\n--- request\nGET /t HTTP/1.0\n--- response_body\nHello World\n--- error_code: 403\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: set ngx.status in access_by_lua_block\ndon't proxy_pass to upstream\n--- config\n    location /t {\n        access_by_lua_block {\n            ngx.status = 403\n            ngx.say(\"Hello World\")\n        }\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u;\n    }\n\n    location /u {\n        content_by_lua_block {\n            ngx.say(\"From upstream\")\n        }\n    }\n--- request\nGET /t HTTP/1.0\n--- response_body\nHello World\n--- error_code: 403\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: set ngx.status in server_rewrite_by_lua_block with sleep\ndon't proxy_pass to upstream\n--- config\n    server_rewrite_by_lua_block {\n        if ngx.var.uri == \"/t\" then\n            ngx.sleep(0.001)\n            ngx.status = 403\n            ngx.say(\"Hello World\")\n        end\n    }\n\n    location /t {\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u;\n    }\n\n    location /u {\n        content_by_lua_block {\n            ngx.say(\"From upstream\")\n        }\n    }\n--- request\nGET /t HTTP/1.0\n--- response_body\nHello World\n--- error_code: 403\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: set ngx.status in rewrite_by_lua_block with sleep\ndon't proxy_pass to upstream\n--- config\n    location /t {\n        rewrite_by_lua_block {\n            ngx.sleep(0.001)\n            ngx.status = 403\n            ngx.say(\"Hello World\")\n        }\n\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u;\n    }\n\n    location /u {\n        content_by_lua_block {\n            ngx.say(\"From upstream\")\n        }\n    }\n--- request\nGET /t HTTP/1.0\n--- response_body\nHello World\n--- error_code: 403\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: set ngx.status in access_by_lua_block\ndon't proxy_pass to upstream\n--- config\n    location /t {\n        access_by_lua_block {\n            ngx.sleep(0.001)\n            ngx.status = 403\n            ngx.say(\"Hello World\")\n        }\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u;\n    }\n\n    location /u {\n        content_by_lua_block {\n            ngx.say(\"From upstream\")\n        }\n    }\n--- request\nGET /t HTTP/1.0\n--- response_body\nHello World\n--- error_code: 403\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/016-resp-header.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 79);\n\n#no_diff();\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: set response content-type header\n--- config\n    location /read {\n        content_by_lua '\n            ngx.header.content_type = \"text/my-plain\";\n            ngx.say(\"Hi\");\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nContent-Type: text/my-plain\n--- response_body\nHi\n\n\n\n=== TEST 2: set response content-type header\n--- config\n    location /read {\n        content_by_lua '\n            ngx.header.content_length = \"text/my-plain\";\n            ngx.say(\"Hi\");\n        ';\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- response_headers\nContent-Type: text/html\n--- error_code: 500\n\n\n\n=== TEST 3: set response content-length header\n--- config\n    location /read {\n        content_by_lua '\n            ngx.header.content_length = 3\n            ngx.say(\"Hello\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nContent-Length: 3\n--- response_body chop\nHel\n--- skip_eval: 3:defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2}) \n\n\n\n=== TEST 4: set response content-type header\n--- config\n    location /read {\n        content_by_lua '\n            ngx.status = 302;\n            ngx.header[\"Location\"] = \"http://agentzh.org/foo\";\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://agentzh.org/foo\n--- response_body\n--- error_code: 302\n\n\n\n=== TEST 5: set response content-type header\n--- config\n    location /read {\n        content_by_lua '\n            ngx.header.content_length = 3\n            ngx.header.content_length = nil\n            ngx.say(\"Hello\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\n!Content-Length\n--- response_body\nHello\n\n\n\n=== TEST 6: set multi response content-type header\n--- config\n    location /read {\n        content_by_lua '\n            ngx.header[\"X-Foo\"] = {\"a\", \"bc\"}\n            ngx.say(\"Hello\")\n        ';\n    }\n--- request\nGET /read\n--- raw_response_headers_like eval\nmy $headers;\n\nif (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    $headers = qr/x-foo: a\\r\\n.*?x-foo: bc\\r\\n/\n} else {\n    $headers = qr/X-Foo: a\\r\\n.*?X-Foo: bc\\r\\n/\n}\n\n$headers;\n--- response_body\nHello\n\n\n\n=== TEST 7: set response content-type header\n--- config\n    location /read {\n        content_by_lua '\n            ngx.header.content_type = {\"a\", \"bc\"}\n            ngx.say(\"Hello\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nContent-Type: bc\n--- response_body\nHello\n\n\n\n=== TEST 8: set multi response content-type header and clears it\n--- config\n    location /read {\n        content_by_lua '\n            ngx.header[\"X-Foo\"] = {\"a\", \"bc\"}\n            ngx.header[\"X-Foo\"] = {}\n            ngx.say(\"Hello\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\n!X-Foo\n--- response_body\nHello\n\n\n\n=== TEST 9: set multi response content-type header and clears it\n--- config\n    location /read {\n        content_by_lua '\n            ngx.header[\"X-Foo\"] = {\"a\", \"bc\"}\n            ngx.header[\"X-Foo\"] = nil\n            ngx.say(\"Hello\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\n!X-Foo\n--- response_body\nHello\n\n\n\n=== TEST 10: set multi response content-type header (multiple times)\n--- config\n    location /read {\n        content_by_lua '\n            ngx.header[\"X-Foo\"] = {\"a\", \"bc\"}\n            ngx.header[\"X-Foo\"] = {\"a\", \"abc\"}\n            ngx.say(\"Hello\")\n        ';\n    }\n--- request\nGET /read\n--- raw_response_headers_like eval\nmy $headers;\n\nif (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    $headers = \"x-foo: a\\r\\n.*?x-foo: abc\\r\\n\"\n} else {\n    $headers = \"X-Foo: a\\r\\n.*?X-Foo: abc\\r\\n\"\n}\n\n$headers;\n--- response_body\nHello\n\n\n\n=== TEST 11: clear first, then add\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header[\"Foo\"] = {}\n            ngx.header[\"Foo\"] = {\"a\", \"b\"}\n            ngx.send_headers()\n        ';\n    }\n--- request\n    GET /lua\n--- raw_response_headers_like eval\nmy $headers;\n\nif (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    $headers = \".*foo: a\\r\nfoo: b.*\";\n} else {\n    $headers = \".*Foo: a\\r\nFoo: b.*\";\n}\n\n$headers;\n--- response_body\n\n\n\n=== TEST 12: first add, then clear, then add again\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header[\"Foo\"] = {\"c\", \"d\"}\n            ngx.header[\"Foo\"] = {}\n            ngx.header[\"Foo\"] = {\"a\", \"b\"}\n            ngx.send_headers()\n        ';\n    }\n--- request\n    GET /lua\n--- raw_response_headers_like eval\nmy $headers;\n\nif (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    $headers = \".*foo: a\\r\nfoo: b.*\";\n} else {\n    $headers = \".*Foo: a\\r\nFoo: b.*\";\n}\n\n$headers;\n--- response_body\n\n\n\n=== TEST 13: names are the same in the beginning (one value per key)\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header[\"Foox\"] = \"barx\"\n            ngx.header[\"Fooy\"] = \"bary\"\n            ngx.send_headers()\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\nFoox: barx\nFooy: bary\n\n\n\n=== TEST 14: names are the same in the beginning (multiple values per key)\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header[\"Foox\"] = {\"conx1\", \"conx2\" }\n            ngx.header[\"Fooy\"] = {\"cony1\", \"cony2\" }\n            ngx.send_headers()\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\nFoox: conx1, conx2\nFooy: cony1, cony2\n\n\n\n=== TEST 15: set header after ngx.print\n--- no_http2\n--- config\n    location /lua {\n        default_type \"text/plain\";\n        content_by_lua '\n            ngx.print(\"hello\")\n            ngx.header.content_type = \"text/foo\"\n        ';\n    }\n--- request\n    GET /lua\n--- response_body chop\nhello\n--- error_log\nattempt to set ngx.header.HEADER after sending out response headers\n--- no_error_log eval\n[\"[alert]\", \"[warn]\"]\n\n\n\n=== TEST 16: get content-type header after ngx.print\n--- config\n    location /lua {\n        default_type \"text/my-plain\";\n        content_by_lua '\n            ngx.print(\"hello, \")\n            ngx.say(ngx.header.content_type)\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\nContent-Type: text/my-plain\n--- response_body\nhello, text/my-plain\n\n\n\n=== TEST 17: get content-length header\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header.content_length = 2;\n            ngx.say(ngx.header.content_length);\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\nContent-Length: 2\n--- response_body\n2\n\n\n\n=== TEST 18: get content-length header\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header.foo = \"bar\";\n            ngx.say(ngx.header.foo);\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\nfoo: bar\n--- response_body\nbar\n\n\n\n=== TEST 19: get content-length header (proxy)\n--- config\n    location /main {\n        set $footer '';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n        header_filter_by_lua '\n            ngx.var.footer = ngx.header.content_length\n        ';\n        echo_after_body $footer;\n    }\n    location /echo {\n        content_by_lua 'ngx.print(\"Hello\")';\n    }\n--- request\n    GET /main\n--- response_headers\n!Content-Length\n--- response_body\nHello5\n\n\n\n=== TEST 20: set and get content-length header (proxy)\n--- config\n    location /main {\n        set $footer '';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n        header_filter_by_lua '\n            ngx.header.content_length = 27\n            ngx.var.footer = ngx.header.content_length\n        ';\n        echo_after_body $footer;\n    }\n    location /echo {\n        content_by_lua 'ngx.print(\"Hello\")';\n    }\n--- request\n    GET /main\n--- response_headers\n!Content-Length\n--- response_body\nHello27\n\n\n\n=== TEST 21: get content-type header (proxy)\n--- config\n    location /main {\n        set $footer '';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n        header_filter_by_lua '\n            ngx.var.footer = ngx.header.content_type\n        ';\n        echo_after_body $footer;\n    }\n    location /echo {\n        default_type 'abc/foo';\n        content_by_lua 'ngx.print(\"Hello\")';\n    }\n--- request\n    GET /main\n--- response_headers\nContent-Type: abc/foo\n--- response_body\nHelloabc/foo\n\n\n\n=== TEST 22: set and get content-type header (proxy)\n--- config\n    location /main {\n        set $footer '';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n        header_filter_by_lua '\n            ngx.header.content_type = \"text/blah\"\n            ngx.var.footer = ngx.header.content_type\n        ';\n        echo_after_body $footer;\n    }\n    location /echo {\n        default_type 'abc/foo';\n        content_by_lua 'ngx.print(\"Hello\")';\n    }\n--- request\n    GET /main\n--- response_headers\nContent-Type: text/blah\n--- response_body\nHellotext/blah\n\n\n\n=== TEST 23: get user header (proxy)\n--- config\n    location /main {\n        set $footer '';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n        header_filter_by_lua '\n            ngx.var.footer = ngx.header.baz\n        ';\n        echo_after_body $footer;\n    }\n    location /echo {\n        content_by_lua '\n            ngx.header.baz = \"bah\"\n            ngx.print(\"Hello\")\n        ';\n    }\n--- request\n    GET /main\n--- response_headers\nbaz: bah\n--- response_body\nHellobah\n\n\n\n=== TEST 24: set and get user header (proxy)\n--- config\n    location /main {\n        set $footer '';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n        header_filter_by_lua '\n            ngx.header.baz = \"foo\"\n            ngx.var.footer = ngx.header.baz\n        ';\n        echo_after_body $footer;\n    }\n    location /echo {\n        content_by_lua '\n            ngx.header.baz = \"bah\"\n            ngx.print(\"Hello\")\n        ';\n    }\n--- request\n    GET /main\n--- response_headers\nbaz: foo\n--- response_body\nHellofoo\n\n\n\n=== TEST 25: get multiple user header (proxy)\n--- config\n    location /main {\n        set $footer '';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n        header_filter_by_lua '\n            ngx.var.footer = table.concat(ngx.header.baz, \", \")\n        ';\n        echo_after_body $footer;\n    }\n    location /echo {\n        content_by_lua '\n            ngx.header.baz = {\"bah\", \"blah\"}\n            ngx.print(\"Hello\")\n        ';\n    }\n--- request\n    GET /main\n--- raw_response_headers_like eval\n\"baz: bah\\r\n.*?baz: blah\"\n--- response_body\nHellobah, blah\n\n\n\n=== TEST 26: set and get multiple user header (proxy)\n--- config\n    location /main {\n        set $footer '';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n        header_filter_by_lua '\n            ngx.header.baz = {\"foo\", \"baz\"}\n            ngx.var.footer = table.concat(ngx.header.baz, \", \")\n        ';\n        echo_after_body $footer;\n    }\n    location /echo {\n        content_by_lua '\n            ngx.header.baz = {\"bah\", \"hah\"}\n            ngx.print(\"Hello\")\n        ';\n    }\n--- request\n    GET /main\n--- raw_response_headers_like eval\n\"baz: foo\\r\n.*?baz: baz\"\n--- response_body\nHellofoo, baz\n\n\n\n=== TEST 27: get non-existent header\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(ngx.header.foo);\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\n!foo\n--- response_body\nnil\n\n\n\n=== TEST 28: get non-existent header\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header.foo = {\"bah\", \"baz\", \"blah\"}\n            ngx.header.foo = nil\n            ngx.say(ngx.header.foo);\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\n!foo\n--- response_body\nnil\n\n\n\n=== TEST 29: override domains in the cookie\n--- config\n    location /foo {\n        echo hello;\n        add_header Set-Cookie 'foo=bar; Domain=backend.int';\n        add_header Set-Cookie 'baz=bah; Domain=backend.int';\n    }\n\n    location /main {\n        proxy_pass http://127.0.0.1:$server_port/foo;\n        header_filter_by_lua '\n            local cookies = ngx.header.set_cookie\n            if not cookies then return end\n            if type(cookies) ~= \"table\" then cookies = {cookies} end\n            local newcookies = {}\n            for i, val in ipairs(cookies) do\n                local newval = string.gsub(val, \"([dD]omain)=[%w_-\\\\\\\\.]+\",\n                          \"%1=external.domain.com\")\n                table.insert(newcookies, newval)\n            end\n            ngx.header.set_cookie = newcookies\n        ';\n    }\n--- request\n    GET /main\n--- response_headers\nSet-Cookie: foo=bar; Domain=external.domain.com, baz=bah; Domain=external.domain.com\n--- response_body\nhello\n\n\n\n=== TEST 30: set single value to cache-control\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header.cache_control = \"private\"\n            ngx.say(\"Cache-Control: \", ngx.var.sent_http_cache_control)\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\nCache-Control: private\n--- response_body\nCache-Control: private\n\n\n\n=== TEST 31: set multi values to cache-control\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header.cache_control = { \"private\", \"no-store\" }\n            ngx.say(\"Cache-Control: \", ngx.var.sent_http_cache_control)\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\nCache-Control: private, no-store\n--- response_body_like chop\n^Cache-Control: private[;,] no-store$\n\n\n\n=== TEST 32: set single value to Link header\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.header.link = \"</foo.jpg>; rel=preload\"\n            ngx.say(\"Link: \", ngx.var.sent_http_link)\n        }\n    }\n--- request\nGET /t\n--- response_headers\nLink: </foo.jpg>; rel=preload\n--- response_body\nLink: </foo.jpg>; rel=preload\n\n\n\n=== TEST 33: set multi values to Link header\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.header.link = {\n                \"</foo.jpg>; rel=preload\",\n                \"</bar.css>; rel=preload; as=style\"\n            }\n\n            ngx.say(\"Link: \", ngx.var.sent_http_link)\n        }\n    }\n--- request\nGET /t\n--- response_headers\nLink: </foo.jpg>; rel=preload, </bar.css>; rel=preload; as=style\n--- response_body_like chop\n^Link: </foo.jpg>; rel=preload[;,] </bar.css>; rel=preload; as=style$\n--- skip_nginx: 3: < 1.13.9\n\n\n\n=== TEST 34: set multi values to cache-control and override it with a single value\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header.cache_control = { \"private\", \"no-store\" }\n            ngx.header.cache_control = { \"no-cache\" }\n            ngx.say(\"Cache-Control: \", ngx.var.sent_http_cache_control)\n            ngx.say(\"Cache-Control: \", ngx.header.cache_control)\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\nCache-Control: no-cache\n--- response_body\nCache-Control: no-cache\nCache-Control: no-cache\n\n\n\n=== TEST 35: set multi values to Link header and override it with a single value\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.header.link = {\n                \"</foo.jpg>; rel=preload\",\n                \"</bar.css>; rel=preload; as=style\"\n            }\n            ngx.header.link = \"</hello.jpg>; rel=preload\"\n            ngx.say(\"Link: \", ngx.var.sent_http_link)\n            ngx.say(\"Link: \", ngx.header.link)\n        }\n    }\n--- request\n    GET /lua\n--- response_headers\nLink: </hello.jpg>; rel=preload\n--- response_body\nLink: </hello.jpg>; rel=preload\nLink: </hello.jpg>; rel=preload\n\n\n\n=== TEST 36: set multi values to cache-control and override it with multiple values\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header.cache_control = { \"private\", \"no-store\" }\n            ngx.header.cache_control = { \"no-cache\", \"blah\", \"foo\" }\n            ngx.say(\"Cache-Control: \", ngx.var.sent_http_cache_control)\n            ngx.say(\"Cache-Control: \", table.concat(ngx.header.cache_control, \", \"))\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\nCache-Control: no-cache, blah, foo\n--- response_body_like chop\n^Cache-Control: no-cache[;,] blah[;,] foo\nCache-Control: no-cache[;,] blah[;,] foo$\n--- no_error_log\n[error]\n\n\n\n=== TEST 37: set multi values to Link header and override it with multiple values\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.header.link = {\n                \"</foo.jpg>; rel=preload\",\n                \"</bar.css>; rel=preload; as=style\"\n            }\n            ngx.header.link = {\n                \"</foo.jpg>; rel=preload\",\n                \"</hello.css>; rel=preload\",\n                \"</bar.css>; rel=preload; as=style\"\n            }\n            ngx.say(\"Link: \", ngx.var.sent_http_link)\n            ngx.say(\"Link: \", table.concat(ngx.header.link, \", \"))\n        }\n    }\n--- request\n    GET /lua\n--- response_headers\nLink: </foo.jpg>; rel=preload, </hello.css>; rel=preload, </bar.css>; rel=preload; as=style\n--- response_body_like chop\n^Link: </foo.jpg>; rel=preload[;,] </hello.css>; rel=preload[;,] </bar.css>; rel=preload; as=style\nLink: </foo.jpg>; rel=preload[;,] </hello.css>; rel=preload[;,] </bar.css>; rel=preload; as=style$\n--- no_error_log\n[error]\n--- skip_nginx: 4: < 1.13.9\n\n\n\n=== TEST 38: set the www-authenticate response header\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header.www_authenticate = \"blah\"\n            ngx.say(\"WWW-Authenticate: \", ngx.var.sent_http_www_authenticate)\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\nWWW-Authenticate: blah\n--- response_body\nWWW-Authenticate: blah\n\n\n\n=== TEST 39: set and clear the www-authenticate response header\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header.foo = \"blah\"\n            ngx.header.foo = nil\n            ngx.say(\"Foo: \", ngx.var.sent_http_foo)\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\n!Foo\n--- response_body\nFoo: nil\n\n\n\n=== TEST 40: set multi values to cache-control and override it with multiple values (to reproduce a bug)\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header.cache_control = { \"private\", \"no-store\", \"foo\", \"bar\", \"baz\" }\n            ngx.header.cache_control = {}\n            ngx.send_headers()\n            ngx.say(\"Cache-Control: \", ngx.var.sent_http_cache_control)\n        ';\n        add_header Cache-Control \"blah\";\n    }\n--- request\n    GET /lua\n--- response_headers\nCache-Control: blah\n--- response_body\nCache-Control: blah\n\n\n\n=== TEST 41: set last-modified and return 304\n--- config\n  location /lua {\n        content_by_lua '\n            ngx.header[\"Last-Modified\"] = ngx.http_time(1290079655)\n            ngx.say(ngx.header[\"Last-Modified\"])\n        ';\n    }\n--- request\n    GET /lua\n--- more_headers\nIf-Modified-Since: Thu, 18 Nov 2010 11:27:35 GMT\n--- response_headers\nLast-Modified: Thu, 18 Nov 2010 11:27:35 GMT\n--- error_code: 304\n\n\n\n=== TEST 42: set last-modified and return 200\n--- config\n  location /lua {\n        content_by_lua '\n            ngx.header[\"Last-Modified\"] = ngx.http_time(1290079655)\n            ngx.say(ngx.header[\"Last-Modified\"])\n        ';\n    }\n--- request\n    GET /lua\n--- more_headers\nIf-Modified-Since: Thu, 18 Nov 2010 11:27:34 GMTT\n--- response_headers\nLast-Modified: Thu, 18 Nov 2010 11:27:35 GMT\n--- response_body\nThu, 18 Nov 2010 11:27:35 GMT\n\n\n\n=== TEST 43: set response content-encoding header should bypass ngx_http_gzip_filter_module\n--- config\n    default_type text/plain;\n    gzip             on;\n    gzip_min_length  1;\n    gzip_types       text/plain;\n    location /read {\n        content_by_lua '\n            ngx.header.content_encoding = \"gzip\";\n            ngx.say(\"Hello, world, my dear friend!\");\n        ';\n    }\n--- request\nGET /read\n--- more_headers\nAccept-Encoding: gzip\n--- response_headers\nContent-Encoding: gzip\n--- no_error_log\n[error]\nhttp gzip filter\n--- response_body\nHello, world, my dear friend!\n\n\n\n=== TEST 44: no transform underscores (write)\n--- config\n    lua_transform_underscores_in_response_headers off;\n    location = /t {\n        content_by_lua '\n            ngx.header.foo_bar = \"Hello\"\n            ngx.say(ngx.header.foo_bar)\n            ngx.say(ngx.header[\"foo-bar\"])\n        ';\n    }\n--- request\n    GET /t\n--- raw_response_headers_like eval\n\"\\r\\nfoo_bar: Hello\\r\\n\"\n--- response_body\nHello\nnil\n\n\n\n=== TEST 45: with transform underscores (write)\n--- config\n    lua_transform_underscores_in_response_headers on;\n    location = /t {\n        content_by_lua '\n            ngx.header.foo_bar = \"Hello\"\n            ngx.say(ngx.header.foo_bar)\n            ngx.say(ngx.header[\"foo-bar\"])\n        ';\n    }\n--- request\n    GET /t\n--- raw_response_headers_like eval\n\"\\r\\nfoo-bar: Hello\\r\\n\"\n--- response_body\nHello\nHello\n\n\n\n=== TEST 46: github issue #199: underscores in lua variables\n--- config\n    location /read {\n        content_by_lua '\n          ngx.header.content_type = \"text/my-plain\"\n\n          local results = {}\n          results.something = \"hello\"\n          results.content_type = \"anything\"\n          results.something_else = \"hi\"\n\n          local arr = {}\n          for k in pairs(results) do table.insert(arr, k) end\n          table.sort(arr)\n          for i, k in ipairs(arr) do\n            ngx.say(k .. \": \" .. results[k])\n          end\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nContent-Type: text/my-plain\n\n--- response_body\ncontent_type: anything\nsomething: hello\nsomething_else: hi\n--- no_error_log\n[error]\n\n\n\n=== TEST 47: set multiple response header\n--- config\n    location /read {\n        content_by_lua '\n            for i = 1, 50 do\n                ngx.header[\"X-Direct-\" .. i] = \"text/my-plain-\" .. i;\n            end\n\n            ngx.say(ngx.header[\"X-Direct-50\"]);\n        ';\n    }\n--- request\nGET /read\n--- response_body\ntext/my-plain-50\n--- no_error_log\n[error]\n\n\n\n=== TEST 48: set multiple response header and then reset and then clear\n--- config\n    location /read {\n        content_by_lua '\n            for i = 1, 50 do\n                ngx.header[\"X-Direct-\" .. i] = \"text/my-plain-\" .. i;\n            end\n\n            for i = 1, 50 do\n                ngx.header[\"X-Direct-\" .. i] = \"text/my-plain\"\n            end\n\n            for i = 1, 50 do\n                ngx.header[\"X-Direct-\" .. i] = nil\n            end\n\n            ngx.say(\"ok\");\n        ';\n    }\n--- request\nGET /read\n--- response_body\nok\n--- no_error_log\n[error]\n--- timeout: 10\n\n\n\n=== TEST 49: set response content-type header for multiple times\n--- config\n    location /read {\n        content_by_lua '\n            ngx.header.content_type = \"text/my-plain\";\n            ngx.header.content_type = \"text/my-plain-2\";\n            ngx.say(\"Hi\");\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nContent-Type: text/my-plain-2\n--- response_body\nHi\n\n\n\n=== TEST 50: set Last-Modified response header for multiple times\n--- config\n    location /read {\n        content_by_lua '\n            ngx.header.last_modified = ngx.http_time(1290079655)\n            ngx.header.last_modified = ngx.http_time(1290079654)\n            ngx.say(\"ok\");\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nLast-Modified: Thu, 18 Nov 2010 11:27:34 GMT\n--- response_body\nok\n\n\n\n=== TEST 51: set Last-Modified response header and then clear\n--- config\n    location /read {\n        content_by_lua '\n            ngx.header.last_modified = ngx.http_time(1290079655)\n            ngx.header.last_modified = nil\n            ngx.say(\"ok\");\n        ';\n    }\n--- request\nGET /read\n--- response_headers\n!Last-Modified\n--- response_body\nok\n\n\n\n=== TEST 52: github #20: segfault caused by the nasty optimization in the nginx core (write)\n--- config\n    location = /t/ {\n        header_filter_by_lua '\n            ngx.header.foo = 1\n        ';\n        proxy_pass http://127.0.0.1:$server_port;\n    }\n--- request\nGET /t\n--- more_headers\nFoo: bar\nBah: baz\n--- response_headers_like\nLocation: https?://localhost:\\d+/t/\n--- response_body_like: 301 Moved Permanently\n--- error_code: 301\n--- no_error_log\n[error]\n\n\n\n=== TEST 53: github #20: segfault caused by the nasty optimization in the nginx core (read)\n--- config\n    location = /t/ {\n        header_filter_by_lua '\n            local v = ngx.header.foo\n        ';\n        proxy_pass http://127.0.0.1:$server_port;\n    }\n--- request\nGET /t\n--- more_headers\nFoo: bar\nBah: baz\n--- response_body_like: 301 Moved Permanently\n--- response_headers_like\nLocation: https?://localhost:\\d+/t/\n--- error_code: 301\n--- no_error_log\n[error]\n\n\n\n=== TEST 54: github #20: segfault caused by the nasty optimization in the nginx core (read Location)\n--- config\n    location = /t/ {\n        header_filter_by_lua '\n            ngx.header.Foo = ngx.header.location\n        ';\n        proxy_pass http://127.0.0.1:$server_port;\n    }\n--- request\nGET /t\n--- more_headers\nFoo: bar\nBah: baz\n--- response_headers_like\nLocation: https?://localhost:\\d+/t/\nFoo: /t/\n--- response_body_like: 301 Moved Permanently\n--- error_code: 301\n--- no_error_log\n[error]\n\n\n\n=== TEST 55: github #20: segfault caused by the nasty optimization in the nginx core (set Foo and read Location)\n--- config\n    location = /t/ {\n        header_filter_by_lua '\n            ngx.header.Foo = 3\n            ngx.header.Foo = ngx.header.location\n        ';\n        proxy_pass http://127.0.0.1:$server_port;\n    }\n--- request\nGET /t\n--- more_headers\nFoo: bar\nBah: baz\n--- response_headers_like\nLocation: https?://localhost:\\d+/t/\nFoo: /t/\n--- response_body_like: 301 Moved Permanently\n--- error_code: 301\n--- no_error_log\n[error]\n\n\n\n=== TEST 56: case sensitive cache-control header\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header[\"cache-Control\"] = \"private\"\n            ngx.say(\"Cache-Control: \", ngx.var.sent_http_cache_control)\n        ';\n    }\n--- request\n    GET /lua\n--- raw_response_headers_like eval\nmy $headers;\n\nif (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    $headers = \"cache-control: private\"\n} else {\n    $headers = \"cache-Control: private\"\n}\n\n$headers;\n--- response_body\nCache-Control: private\n\n\n\n=== TEST 57: case sensitive Link header\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.header[\"link\"] = \"</foo.jpg>; rel=preload\"\n            ngx.say(\"Link: \", ngx.var.sent_http_link)\n        }\n    }\n--- request\n    GET /lua\n--- raw_response_headers_like chop\nlink: </foo.jpg>; rel=preload\n--- response_body\nLink: </foo.jpg>; rel=preload\n\n\n\n=== TEST 58: clear Cache-Control when there was no Cache-Control\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header[\"Cache-Control\"] = nil\n            ngx.say(\"Cache-Control: \", ngx.var.sent_http_cache_control)\n        ';\n    }\n--- request\n    GET /lua\n--- raw_response_headers_unlike eval\nqr/Cache-Control/i\n--- response_body\nCache-Control: nil\n\n\n\n=== TEST 59: clear Link header when there was no Link\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.header[\"Link\"] = nil\n            ngx.say(\"Link: \", ngx.var.sent_http_link)\n        }\n    }\n--- request\n    GET /lua\n--- raw_response_headers_unlike eval\nqr/Link/i\n--- response_body\nLink: nil\n\n\n\n=== TEST 60: set response content-type header\n--- config\n    location /read {\n        content_by_lua '\n            local s = \"content_type\"\n            local v = ngx.header[s]\n            ngx.say(\"s = \", s)\n        ';\n    }\n--- request\nGET /read\n--- response_body\ns = content_type\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 61: set a number header name\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header[32] = \"private\"\n            ngx.say(\"32: \", ngx.var.sent_http_32)\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\n32: private\n--- response_body\n32: private\n--- no_error_log\n[error]\n\n\n\n=== TEST 62: set a number header name (in a table value)\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header.foo = {32}\n            ngx.say(\"foo: \", ngx.var.sent_http_foo)\n        ';\n    }\n--- request\n    GET /lua\n--- response_headers\nfoo: 32\n--- response_body\nfoo: 32\n--- no_error_log\n[error]\n\n\n\n=== TEST 63: random access resp headers\n--- config\n    location /resp-header {\n        content_by_lua '\n            ngx.header[\"Foo\"] = \"bar\"\n            ngx.header[\"Bar\"] = \"baz\"\n            local headers, err = ngx.resp.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(\"Foo: \", headers[\"Foo\"] or \"nil\")\n            ngx.say(\"foo: \", headers[\"foo\"] or \"nil\")\n            ngx.say(\"Bar: \", headers[\"Bar\"] or \"nil\")\n\n            headers, err = ngx.resp.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(\"bar: \", headers[\"bar\"] or \"nil\")\n        ';\n    }\n--- request\nGET /resp-header\n--- response_headers\nFoo: bar\nBar: baz\n--- response_body\nFoo: bar\nfoo: bar\nBar: baz\nbar: baz\n--- no_error_log\n[error]\n\n\n\n=== TEST 64: iterating through raw resp headers\n--- config\n    location /resp-header {\n        content_by_lua '\n            ngx.header[\"Foo\"] = \"bar\"\n            ngx.header[\"Bar\"] = \"baz\"\n\n            local headers, err = ngx.resp.get_headers(nil, true)\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            local h = {}\n            for k, v in pairs(headers) do\n                h[k] = v\n            end\n            ngx.say(\"Foo: \", h[\"Foo\"] or \"nil\")\n            ngx.say(\"foo: \", h[\"foo\"] or \"nil\")\n            ngx.say(\"Bar: \", h[\"Bar\"] or \"nil\")\n            ngx.say(\"bar: \", h[\"bar\"] or \"nil\")\n        ';\n    }\n--- request\nGET /resp-header\n--- response_headers\nFoo: bar\nBar: baz\n--- response_body\nFoo: bar\nfoo: nil\nBar: baz\nbar: nil\n\n\n\n=== TEST 65: removed response headers\n--- config\n    location /resp-header {\n        content_by_lua '\n            ngx.header[\"Foo\"] = \"bar\"\n            ngx.header[\"Foo\"] = nil\n            ngx.header[\"Bar\"] = \"baz\"\n\n            local headers, err = ngx.resp.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(\"Foo: \", headers[\"Foo\"] or \"nil\")\n            ngx.say(\"foo: \", headers[\"foo\"] or \"nil\")\n            ngx.say(\"Bar: \", headers[\"Bar\"] or \"nil\")\n            ngx.say(\"bar: \", headers[\"bar\"] or \"nil\")\n        ';\n    }\n--- request\nGET /resp-header\n--- response_headers\n!Foo\nBar: baz\n--- response_body\nFoo: nil\nfoo: nil\nBar: baz\nbar: baz\n\n\n\n=== TEST 66: built-in Content-Type header\n--- config\n    location = /t {\n        content_by_lua '\n            ngx.say(\"hi\")\n        ';\n\n        header_filter_by_lua '\n            local hs, err = ngx.resp.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            print(\"my Content-Type: \", hs[\"Content-Type\"])\n            print(\"my content-type: \", hs[\"content-type\"])\n            print(\"my content_type: \", hs[\"content_type\"])\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nhi\n--- no_error_log\n[error]\n[alert]\n--- error_log\nmy Content-Type: text/plain\nmy content-type: text/plain\nmy content_type: text/plain\n\n\n\n=== TEST 67: built-in Content-Length header\n--- config\n    location = /t {\n        content_by_lua '\n            ngx.say(\"hi\")\n        ';\n\n        header_filter_by_lua '\n            local hs, err = ngx.resp.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            print(\"my Content-Length: \", hs[\"Content-Length\"])\n            print(\"my content-length: \", hs[\"content-length\"])\n            print(\"my content_length: \", hs.content_length)\n        ';\n    }\n--- request\n    GET /t HTTP/1.0\n--- response_body\nhi\n--- no_error_log\n[error]\n[alert]\n--- error_log\nmy Content-Length: 3\nmy content-length: 3\nmy content_length: 3\n\n\n\n=== TEST 68: built-in Connection header\n--- config\n    location = /t {\n        content_by_lua '\n            ngx.say(\"hi\")\n        ';\n\n        header_filter_by_lua '\n            local hs, err = ngx.resp.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            print(\"my Connection: \", hs[\"Connection\"])\n            print(\"my connection: \", hs[\"connection\"])\n        ';\n    }\n--- request\n    GET /t HTTP/1.0\n--- response_body\nhi\n--- no_error_log\n[error]\n[alert]\n--- error_log\nmy Connection: close\nmy connection: close\n\n\n\n=== TEST 69: built-in Transfer-Encoding header (chunked)\n--- config\n    location = /t {\n        content_by_lua '\n            ngx.say(\"hi\")\n        ';\n\n        body_filter_by_lua '\n            local hs, err = ngx.resp.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            print(\"my Transfer-Encoding: \", hs[\"Transfer-Encoding\"])\n            print(\"my transfer-encoding: \", hs[\"transfer-encoding\"])\n            print(\"my transfer_encoding: \", hs.transfer_encoding)\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nhi\n--- no_error_log\n[error]\n[alert]\n--- error_log\nmy Transfer-Encoding: chunked\nmy transfer-encoding: chunked\n--- skip_eval: 6:defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2}) \n\n\n\n=== TEST 70: built-in Transfer-Encoding header (none)\n--- config\n    location = /t {\n        content_by_lua '\n            ngx.say(\"hi\")\n        ';\n\n        body_filter_by_lua '\n            local hs, err = ngx.resp.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            print(\"my Transfer-Encoding: \", hs[\"Transfer-Encoding\"])\n            print(\"my transfer-encoding: \", hs[\"transfer-encoding\"])\n            print(\"my transfer_encoding: \", hs.transfer_encoding)\n        ';\n    }\n--- request\n    GET /t HTTP/1.0\n--- response_body\nhi\n--- no_error_log\n[error]\n[alert]\n--- error_log\nmy Transfer-Encoding: nil\nmy transfer-encoding: nil\nmy transfer_encoding: nil\n\n\n\n=== TEST 71: set Location (no host)\n--- config\n    location = /t {\n        content_by_lua '\n            ngx.header.location = \"/foo/bar\"\n            return ngx.exit(301)\n        ';\n    }\n--- request\nGET /t\n--- response_headers\nLocation: /foo/bar\n--- response_body_like: 301 Moved Permanently\n--- error_code: 301\n--- no_error_log\n[error]\n\n\n\n=== TEST 72: set Location (with host)\n--- config\n    location = /t {\n        content_by_lua '\n            ngx.header.location = \"http://test.com/foo/bar\"\n            return ngx.exit(301)\n        ';\n    }\n--- request\nGET /t\n--- response_headers\nLocation: http://test.com/foo/bar\n--- response_body_like: 301 Moved Permanently\n--- error_code: 301\n--- no_error_log\n[error]\n\n\n\n=== TEST 73: ngx.header[\"Content-Type\"] with ngx_gzip\n--- config\n    gzip             on;\n    gzip_min_length  1;\n    location = /test2 {\n        content_by_lua '\n            ngx.header[\"Content-Type\"] = \"text/html; charset=utf-8\"\n            ngx.say(\"test\")\n        ';\n    }\n--- request\nGET /test2\n--- more_headers\nAccept-Encoding: gzip\n--- response_headers\nContent-Encoding: gzip\nContent-Type: text/html; charset=utf-8\n--- response_body_like chomp\n[^[:ascii:]]+\n--- no_error_log\n[error]\n\n\n\n=== TEST 74: ngx.header[\"Content-Type\"] with \"; blah\"\n--- config\n    location = /test2 {\n        content_by_lua '\n            ngx.header[\"Content-Type\"] = \"; blah\"\n            ngx.say(\"test\")\n        ';\n    }\n--- request\nGET /test2\n--- response_headers\n!Content-Encoding\nContent-Type: ; blah\n--- response_body\ntest\n--- no_error_log\n[error]\n\n\n\n=== TEST 75: exceeding max header limit (default 100)\n--- config\n    location /resp-header {\n        content_by_lua_block {\n            for i = 1, 100 do\n                ngx.header[\"Foo\" .. i] = \"Foo\"\n            end\n\n            local headers, err = ngx.resp.get_headers()\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local cnt = 0\n            for k, v in pairs(headers) do\n                cnt = cnt + 1\n            end\n\n            ngx.say(\"found \", cnt, \" resp headers\");\n        }\n    }\n--- request\nGET /resp-header\n--- response_body\nerr: truncated\nfound 100 resp headers\n--- no_error_log\n[error]\n--- log_level: debug\n--- error_log\nlua exceeding response header limit 101 > 100\n\n\n\n=== TEST 76: NOT exceeding max header limit (default 100)\n--- config\n    location /resp-header {\n        content_by_lua_block {\n            for i = 1, 99 do\n                ngx.header[\"Foo\" .. i] = \"Foo\"\n            end\n\n            local headers, err = ngx.resp.get_headers()\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local cnt = 0\n            for k, v in pairs(headers) do\n                cnt = cnt + 1\n            end\n\n            ngx.say(\"found \", cnt, \" resp headers\");\n        }\n    }\n--- request\nGET /resp-header\n--- response_body\nfound 100 resp headers\n--- no_error_log\n[error]\nlua exceeding response header limit\n--- log_level: debug\n\n\n\n=== TEST 77: exceeding max header limit (custom limit, 3)\n--- config\n    location /resp-header {\n        content_by_lua_block {\n            for i = 1, 3 do\n                ngx.header[\"Foo\" .. i] = \"Foo\"\n            end\n\n            local headers, err = ngx.resp.get_headers(3)\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local cnt = 0\n            for k, v in pairs(headers) do\n                cnt = cnt + 1\n            end\n\n            ngx.say(\"found \", cnt, \" resp headers\");\n        }\n    }\n--- request\nGET /resp-header\n--- response_body\nerr: truncated\nfound 3 resp headers\n--- no_error_log\n[error]\n--- error_log\nlua exceeding response header limit 4 > 3\n--- log_level: debug\n\n\n\n=== TEST 78: NOT exceeding max header limit (custom limit, 3)\n--- config\n    location /resp-header {\n        content_by_lua_block {\n            for i = 1, 2 do\n                ngx.header[\"Foo\" .. i] = \"Foo\"\n            end\n\n            local headers, err = ngx.resp.get_headers(3)\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local cnt = 0\n            for k, v in pairs(headers) do\n                cnt = cnt + 1\n            end\n\n            ngx.say(\"found \", cnt, \" resp headers\");\n        }\n    }\n--- request\nGET /resp-header\n--- response_body\nfound 3 resp headers\n--- no_error_log\n[error]\nlua exceeding response header limit\n\n\n\n=== TEST 79: return nil if Content-Type is not set yet\n--- config\n    location /t {\n        default_type text/html;\n        content_by_lua_block {\n            ngx.log(ngx.WARN, \"Content-Type: \", ngx.header[\"content-type\"])\n            ngx.say(\"Content-Type: \", ngx.header[\"content-type\"])\n        }\n    }\n--- request\nGET /t\n--- response_headers\nContent-Type: text/html\n--- response_body\nContent-Type: nil\n--- no_error_log\n[error]\n--- error_log\nContent-Type: nil\n\n\n\n=== TEST 80: don't generate Content-Type when setting other response header\n--- config\n    location = /backend {\n        content_by_lua_block {\n            ngx.say(\"foo\")\n        }\n        header_filter_by_lua_block {\n            ngx.header.content_type = nil\n        }\n    }\n\n    location = /t {\n        default_type text/html;\n        rewrite_by_lua_block {\n            ngx.header.blah = \"foo\"\n        }\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/backend;\n    }\n--- request\nGET /t\n--- response_body\nfoo\n--- response_headers\nblah: foo\n!Content-Type\n--- no_error_log\n[error]\n\n\n\n=== TEST 81: don't generate Content-Type when getting other response header\n--- config\n    location = /backend {\n        content_by_lua_block {\n            ngx.say(\"foo\")\n        }\n        header_filter_by_lua_block {\n            ngx.header.content_type = nil\n        }\n    }\n\n    location = /t {\n        default_type text/html;\n        rewrite_by_lua_block {\n            local h = ngx.header.content_length\n        }\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/backend;\n    }\n--- request\nGET /t\n--- response_body\nfoo\n--- response_headers\n!Content-Type\n--- no_error_log\n[error]\n\n\n\n=== TEST 82: don't generate Content-Type when getting it\n--- config\n    location = /backend {\n        content_by_lua_block {\n            ngx.say(\"foo\")\n        }\n        header_filter_by_lua_block {\n            ngx.header.content_type = nil\n        }\n    }\n\n    location /t {\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/backend;\n        header_filter_by_lua_block {\n            ngx.log(ngx.WARN, \"Content-Type: \", ngx.header[\"content-type\"])\n        }\n    }\n--- request\nGET /t\n--- response_body\nfoo\n--- response_headers\n!Content-Type\n--- no_error_log\n[error]\n--- error_log\nContent-Type: nil\n\n\n\n=== TEST 83: generate default Content-Type when setting other response header\n--- config\n    location = /t {\n        default_type text/html;\n        content_by_lua_block {\n            ngx.header.blah = \"foo\"\n            ngx.say(\"foo\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nfoo\n--- response_headers\nblah: foo\nContent-Type: text/html\n--- no_error_log\n[error]\n\n\n\n=== TEST 84: don't generate Content-Type when calling ngx.resp.get_headers()\n--- config\n    location = /backend {\n        content_by_lua_block {\n            ngx.say(\"foo\")\n        }\n        header_filter_by_lua_block {\n            ngx.header.content_type = nil\n        }\n    }\n\n    location /t {\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/backend;\n        header_filter_by_lua_block {\n            local h, err = ngx.resp.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return\n            end\n\n            ngx.log(ngx.WARN, \"Content-Type: \", h[\"content-type\"])\n        }\n    }\n--- request\nGET /t\n--- response_body\nfoo\n--- response_headers\n!Content-Type\n--- no_error_log\n[error]\n--- error_log\nContent-Type: nil\n\n\n\n=== TEST 85: don't generate default Content-Type when Content-Type is cleared\n--- config\n    location = /t {\n        default_type text/html;\n        content_by_lua_block {\n            ngx.header[\"Content-Type\"] = nil\n            ngx.say(\"foo\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nfoo\n--- response_headers\n!Content-Type\n--- no_error_log\n[error]\n\n\n\n=== TEST 86: don't generate default Content-Type when Content-Type is set\n--- config\n    location = /t {\n        default_type text/html;\n        content_by_lua_block {\n            ngx.header[\"Content-Type\"] = \"application/json\"\n            ngx.say(\"foo\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nfoo\n--- response_headers\nContent-Type: application/json\n--- no_error_log\n[error]\n\n\n\n=== TEST 87: unsafe header value (with '\\r')\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.header.header = \"value\\rfoo:bar\\nbar:foo\"\n            ngx.say(\"foo\")\n        }\n    }\n--- request\nGET /t\n--- response_headers\nheader: value%0Dfoo:bar%0Abar:foo\nfoo:\nbar:\n--- no_error_log\n[error]\n\n\n\n=== TEST 88: unsafe header value (with '\\n')\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.header.header = \"value\\nfoo:bar\\rbar:foo\"\n            ngx.say(\"foo\")\n        }\n    }\n--- request\nGET /t\n--- response_headers\nheader: value%0Afoo:bar%0Dbar:foo\nfoo:\nbar:\n--- no_error_log\n[error]\n\n\n\n=== TEST 89: unsafe header name (with '\\r')\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.header[\"header: value\\rfoo:bar\\nbar:foo\"] = \"xx\"\n            ngx.say(\"foo\")\n        }\n    }\n--- request\nGET /t\n--- response_headers\nheader%3A%20value%0Dfoo%3Abar%0Abar%3Afoo: xx\nheader:\nfoo:\nbar:\n--- no_error_log\n[error]\n\n\n\n=== TEST 90: unsafe header name (with '\\n')\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.header[\"header: value\\nfoo:bar\\rbar:foo\"] = \"xx\"\n            ngx.say(\"foo\")\n        }\n    }\n--- request\nGET /t\n--- response_headers\nheader%3A%20value%0Afoo%3Abar%0Dbar%3Afoo: xx\nheader:\nfoo:\nbar:\n--- no_error_log\n[error]\n\n\n\n=== TEST 91: unsafe header name (with prefix '\\r')\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.header[\"\\rheader: value\\rfoo:bar\\nbar:foo\"] = \"xx\"\n            ngx.say(\"foo\")\n        }\n    }\n--- request\nGET /t\n--- response_headers\n%0Dheader%3A%20value%0Dfoo%3Abar%0Abar%3Afoo: xx\nheader:\nfoo:\nbar:\n--- no_error_log\n[error]\n\n\n\n=== TEST 92: unsafe header name (with prefix '\\n')\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.header[\"\\nheader: value\\nfoo:bar\\rbar:foo\"] = \"xx\"\n            ngx.say(\"foo\")\n        }\n    }\n--- request\nGET /t\n--- response_headers\n%0Aheader%3A%20value%0Afoo%3Abar%0Dbar%3Afoo: xx\nheader:\nfoo:\nbar:\n--- no_error_log\n[error]\n\n\n\n=== TEST 93: multiple unsafe header values (with '\\n' and '\\r')\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.header[\"foo\"] = {\n                \"foo\\nxx:bar\",\n                \"bar\\rxxx:foo\",\n            }\n            ngx.say(\"foo\")\n        }\n    }\n--- request\nGET /t\n--- response_headers\nxx:\nxxx:\n--- raw_response_headers_like chomp\nfoo: foo%0Axx:bar\\r\\nfoo: bar%0Dxxx:foo\\r\\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 94: fix negative content-length number(#1791)\n--- config\n    location = /big-upstream {\n        content_by_lua_block {\n            ngx.header['Content-Length'] = math.pow(2, 33) - 1\n            ngx.say('hi')\n        }\n    }\n\n    location = /t {\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/big-upstream;\n        proxy_buffering off;\n\n        header_filter_by_lua_block {\n            local hs, err = ngx.resp.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            print(\"my Content-Length: \", hs[\"Content-Length\"])\n\n            ngx.header['Content-Length'] = 3\n        }\n    }\n--- request\n    GET /t\n--- response_body\nhi\n--- no_error_log\n[alert]\n--- error_log eval\n[ \"my Content-Length: 8589934591\",\nqr/upstream prematurely closed connection while sending to client|upstream prematurely closed connection while reading upstream/]\n\n\n\n=== TEST 95: Expose the 'Last-Modified' response header as ngx.header[\"Last-Modified\"]\n--- config\n    location /a.txt {\n        header_filter_by_lua_block {\n            local last_modified = ngx.header[\"Last-Modified\"]\n            if last_modified == nil then\n                ngx.log(ngx.ERR, \"can not get lasted modified\")\n                ngx.exit(500)\n                return\n            end\n\n            local last_mod = ngx.parse_http_time(last_modified)\n            local age = ngx.time() - last_mod\n            ngx.header[\"Age\"] = age\n        }\n    }\n--- user_files\n>>> a.txt\nFoo\n--- request\nGET /a.txt\n--- raw_response_headers_like eval\nqr/^(a|A)ge: \\d\\r\\n/ms\n--- no_error_log\n[error]\n\n\n\n=== TEST 96: 'Last-Modified' from upstream\n--- config\n    location /test/ {\n        proxy_pass http://127.0.0.1:$server_port/;\n\n        header_filter_by_lua_block {\n            local last_modified = ngx.header[\"Last-Modified\"]\n            if last_modified == nil then\n                ngx.log(ngx.ERR, \"can not get lasted modified\")\n                ngx.exit(500)\n                return\n            end\n\n            local last_mod = ngx.parse_http_time(last_modified)\n            local age = ngx.time() - last_mod\n            ngx.header[\"Age\"] = age\n        }\n    }\n\n--- user_files\n>>> a.txt\nFoo\n--- request\nGET /test/a.txt\n--- raw_response_headers_like eval\nqr/^(a|A)ge: \\d\\r\\n/ms\n--- no_error_log\n[error]\n\n\n\n=== TEST 97: 'Last-Modified' does not exist\n--- config\n    location /test {\n        header_filter_by_lua_block {\n            local last_modified = ngx.header[\"Last-Modified\"]\n            if last_modified == nil then\n                ngx.log(ngx.INFO, \"Last-Modified is nil as expected\")\n                return\n            end\n\n            ngx.log(ngx.ERR, \"Last-Modified expected to be nil, but got \", last_modified)\n        }\n\n        content_by_lua_block {\n            ngx.say(\"Hello World\")\n        }\n    }\n--- request\nGET /test\n--- response_body\nHello World\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/017-exec.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 8);\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\n#no_diff();\n#no_shuffle();\n#no_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /read {\n        content_by_lua '\n            ngx.exec(\"/hi\");\n            ngx.say(\"Hi\");\n        ';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body\nHello\n\n\n\n=== TEST 2: empty uri arg\n--- config\n    location /read {\n        content_by_lua '\n            ngx.exec(\"\");\n            ngx.say(\"Hi\");\n        ';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 3: no arg\n--- config\n    location /read {\n        content_by_lua '\n            ngx.exec();\n            ngx.say(\"Hi\");\n        ';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 4: too many args\n--- config\n    location /read {\n        content_by_lua '\n            ngx.exec(1, 2, 3, 4);\n            ngx.say(\"Hi\");\n        ';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 5: null uri\n--- config\n    location /read {\n        content_by_lua '\n            ngx.exec(nil)\n            ngx.say(\"Hi\")\n        ';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 6: user args\n--- config\n    location /read {\n        content_by_lua '\n            ngx.exec(\"/hi\", \"Yichun Zhang\")\n            ngx.say(\"Hi\")\n        ';\n    }\n    location /hi {\n        echo Hello $query_string;\n    }\n--- request\nGET /read\n--- response_body\nHello Yichun Zhang\n\n\n\n=== TEST 7: args in uri\n--- config\n    location /read {\n        content_by_lua '\n            ngx.exec(\"/hi?agentzh\")\n            ngx.say(\"Hi\")\n        ';\n    }\n    location /hi {\n        echo Hello $query_string;\n    }\n--- request\nGET /read\n--- response_body\nHello agentzh\n\n\n\n=== TEST 8: args in uri and user args\n--- config\n    location /read {\n        content_by_lua '\n            ngx.exec(\"/hi?a=Yichun\", \"b=Zhang\")\n            ngx.say(\"Hi\")\n        ';\n    }\n    location /hi {\n        echo Hello $query_string;\n    }\n--- request\nGET /read\n--- response_body\nHello a=Yichun&b=Zhang\n\n\n\n=== TEST 9: args in uri and user args\n--- config\n    location /read {\n        content_by_lua '\n            ngx.exec(\"@hi?a=Yichun\", \"b=Zhang\")\n            ngx.say(\"Hi\")\n        ';\n    }\n    location @hi {\n        echo Hello $query_string;\n    }\n--- request\nGET /read\n--- response_body\nHello \n\n\n\n=== TEST 10: exec after location capture (simple echo)\n--- config\n    location /test {\n        content_by_lua_file 'html/test.lua';\n    }\n\n    location /a {\n        echo \"hello\";\n    }\n\n    location /b {\n        echo \"hello\";\n    }\n\n--- user_files\n>>> test.lua\nngx.location.capture('/a')\n\nngx.exec('/b')\n--- request\n    GET /test\n--- response_body\nhello\n\n\n\n=== TEST 11: exec after location capture (memc)\n--- config\n    location /test {\n        content_by_lua_file 'html/test.lua';\n    }\n\n    location /a {\n        set $memc_key 'hello world';\n        set $memc_value 'hello hello hello world world world';\n        set $memc_cmd set;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /b {\n        set $memc_key 'hello world';\n        set $memc_cmd get;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n--- user_files\n>>> test.lua\nngx.location.capture('/a')\n\nngx.exec('/b')\n--- request\n    GET /test\n--- response_body: hello hello hello world world world\n\n\n\n=== TEST 12: exec after named location capture (simple echo)\n--- config\n    location /test {\n        content_by_lua_file 'html/test.lua';\n    }\n\n    location /a {\n        echo \"hello\";\n    }\n\n    location @b {\n        echo \"hello\";\n    }\n\n--- user_files\n>>> test.lua\nngx.location.capture('/a')\n\nngx.exec('@b')\n--- request\n    GET /test\n--- response_body\nhello\n\n\n\n=== TEST 13: exec after named location capture (memc)\n--- config\n    location /test {\n        content_by_lua_file 'html/test.lua';\n    }\n\n    location /a {\n        set $memc_key 'hello world';\n        set $memc_value 'hello hello hello world world world';\n        set $memc_cmd set;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location @b {\n        set $memc_key 'hello world';\n        set $memc_cmd get;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n--- user_files\n>>> test.lua\nngx.location.capture('/a')\n\nngx.exec('@b')\n--- request\n    GET /test\n--- response_body: hello hello hello world world world\n\n\n\n=== TEST 14: github issue #40: 2 Subrequest calls when using access_by_lua, ngx.exec and echo_location (content)\n--- config\n    location = /hi {\n        echo hello;\n    }\n    location /sub {\n        proxy_pass http://127.0.0.1:$server_port/hi;\n        #echo hello;\n    }\n    location /p{\n        #content_by_lua '\n            #local res = ngx.location.capture(\"/sub\")\n            #ngx.print(res.body)\n        #';\n        echo_location /sub;\n    }\n    location /lua {\n        content_by_lua '\n            ngx.exec(\"/p\")\n        ';\n    }\n--- request\n    GET /lua\n--- response_body\nhello\n\n\n\n=== TEST 15: github issue #40: 2 Subrequest calls when using access_by_lua, ngx.exec and echo_location (content + named location)\n--- config\n    location = /hi {\n        echo hello;\n    }\n    location /sub {\n        proxy_pass http://127.0.0.1:$server_port/hi;\n        #echo hello;\n    }\n    location @p {\n        #content_by_lua '\n            #local res = ngx.location.capture(\"/sub\")\n            #ngx.print(res.body)\n        #';\n        echo_location /sub;\n    }\n    location /lua {\n        content_by_lua '\n            ngx.exec(\"@p\")\n        ';\n    }\n--- request\n    GET /lua\n--- response_body\nhello\n\n\n\n=== TEST 16: github issue #40: 2 Subrequest calls when using access_by_lua, ngx.exec and echo_location (content + post subrequest)\n--- config\n    location = /hi {\n        echo hello;\n    }\n    location /sub {\n        proxy_pass http://127.0.0.1:$server_port/hi;\n        #echo hello;\n    }\n    location /p{\n        #content_by_lua '\n            #local res = ngx.location.capture(\"/sub\")\n            #ngx.print(res.body)\n        #';\n        echo_location /sub;\n    }\n    location blah {\n        echo blah;\n    }\n    location /lua {\n        content_by_lua '\n            ngx.location.capture(\"/blah\")\n            ngx.exec(\"/p\")\n        ';\n    }\n--- request\n    GET /lua\n--- response_body\nhello\n\n\n\n=== TEST 17: pcall safe\n--- config\n    location /lua {\n        content_by_lua '\n            local function f ()\n                ngx.exec(\"/hi\")\n            end\n\n            pcall(f)\n            ngx.say(\"hello\")\n        ';\n    }\n    location /hi {\n        echo hi;\n    }\n--- request\nGET /lua\n--- error_code: 200\n--- response_body\nhi\n\n\n\n=== TEST 18: lua table as \"args\" parameter\n--- config\n    location /lua {\n        content_by_lua '\n            local args = { foo = 3, bar = 4 }\n            ngx.exec(\"/hi\", args)\n        ';\n    }\n    location /hi {\n        echo \"foo = $arg_foo\";\n        echo \"bar = $arg_bar\";\n    }\n--- request\nGET /lua\n--- error_code: 200\n--- response_body\nfoo = 3\nbar = 4\n\n\n\n=== TEST 19: jump to internal locations requires ctx cleared\n--- config\n    location @proxy {\n        rewrite_by_lua return;\n        echo hello;\n    }\n    location /main {\n        content_by_lua '\n            ngx.exec(\"@proxy\")\n        ';\n    }\n--- request\n    GET /main\n--- response_body\nhello\n\n\n\n=== TEST 20: exec + rewrite + named locations\n--- config\n    location @proxy {\n        rewrite_by_lua return;\n        echo hello;\n    }\n    location /main {\n        rewrite_by_lua '\n            ngx.exec(\"@proxy\")\n        ';\n    }\n--- request\n    GET /main\n--- response_body\nhello\n\n\n\n=== TEST 21: exec(named location) in subrequests\n--- config\n    location /entry {\n        echo_location /foo;\n        echo_location /foo2;\n    }\n  location /foo {\n      content_by_lua '\n          ngx.exec(\"@bar\")\n      ';\n  }\n  location /foo2 {\n      content_by_lua '\n          ngx.exec(\"@bar\")\n      ';\n  }\n\n  location @bar {\n      proxy_pass http://127.0.0.1:$server_port/bar;\n  }\n  location /bar {\n      echo hello;\n  }\n--- request\n    GET /entry\n--- response_body\nhello\nhello\n\n\n\n=== TEST 22: exec(normal location) in subrequests\n--- config\n    location /entry {\n        echo_location /foo;\n        echo_location /foo2;\n    }\n  location /foo {\n      content_by_lua '\n          ngx.exec(\"/baz\")\n      ';\n  }\n  location /foo2 {\n      content_by_lua '\n          ngx.exec(\"/baz\")\n      ';\n  }\n\n  location /baz {\n      proxy_pass http://127.0.0.1:$server_port/bar;\n  }\n  location /bar {\n      echo hello;\n  }\n--- request\n    GET /entry\n--- response_body\nhello\nhello\n\n\n\n=== TEST 23: content_by_lua + ngx.exec + subrequest capture\n--- config\n    location /main {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/test_loc\");\n            ngx.print(\"hello, \", res.body)\n        ';\n        content_by_lua return;\n    }\n    location /test_loc {\n        content_by_lua '\n            ngx.exec(\"@proxy\")\n        ';\n    }\n    location @proxy {\n        #echo proxy;\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n    location /foo {\n        #echo_status 201;\n        echo bah;\n    }\n--- request\n    GET /main\n--- response_body\nhello, bah\n\n\n\n=== TEST 24: jump to an internal location\n--- config\n    location /t {\n        content_by_lua '\n            return ngx.exec(\"/proxy\", ngx.var.args)\n        ';\n    }\n\n    location /proxy {\n        internal;\n\n        proxy_pass http://127.0.0.1:$server_port/dummy;\n    }\n\n    location = /dummy {\n        echo -n dummy;\n    }\n--- pipelined_requests eval\n[\"GET /t\", \"GET /t?foo\"]\n--- response_body eval\n[\"dummy\", \"dummy\"]\n--- no_error_log\n[error]\n\n\n\n=== TEST 25: pipelined requests\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.exec(\"@foo\")\n        }\n    }\n\n    location @foo {\n        return 200;\n    }\n--- pipelined_requests eval\n[\"GET /t\", \"GET /t\"]\n--- error_code eval\n[200, 200]\n--- response_body eval\n[\"\", \"\"]\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/018-ndk.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 4);\n\n#no_diff();\n#no_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /read {\n        content_by_lua '\n            local s = ndk.set_var.set_escape_uri(\" :\")\n            local r = ndk.set_var.set_unescape_uri(\"a%20b\")\n            ngx.say(s)\n            ngx.say(r)\n        ';\n    }\n--- request\nGET /read\n--- response_body\n%20%3A\na b\n\n\n\n=== TEST 2: directive not found\n--- config\n    location /read {\n        content_by_lua '\n            local s = ndk.set_var.set_escape_uri_blah_blah(\" :\")\n            ngx.say(s)\n        ';\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 3: directive not found\n--- config\n    location /read {\n        content_by_lua '\n            local s = ndk.set_var.content_by_lua(\" :\")\n            ngx.say(s)\n        ';\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 4: directive not found\n--- config\n    location /read {\n        header_filter_by_lua '\n            ngx.header.Foo = ndk.set_var.set_escape_uri(\" %\")\n        ';\n        echo hi;\n    }\n--- request\nGET /read\n--- response_headers\nFoo: %20%25\n--- response_body\nhi\n\n\n\n=== TEST 5: bug: ndk.set_var not initialize ngx_http_variable_value_t variable properly\n--- config\n   location /luaset {\n     content_by_lua \"\n\n       local version = '2011.10.13+0000'\n       local e_version = ndk.set_var.set_encode_base32(version)\n       local s_version= ndk.set_var.set_quote_sql_str(version)\n       ngx.say(e_version)\n       ngx.say(s_version)\n     \";\n   }\n--- request\nGET /luaset\n--- response_body\n68o32c9e64o2sc9j5co30c1g\n'2011.10.13+0000'\n\n\n\n=== TEST 6: set_by_lua\n--- config\n    location /read {\n        set_by_lua $r '\n            return ndk.set_var.set_unescape_uri(\"a%20b\")\n        ';\n        echo $r;\n    }\n--- request\nGET /read\n--- response_body\na b\n\n\n\n=== TEST 7: header_filter_by_lua\n--- config\n    location /read {\n        set $foo '';\n        content_by_lua '\n            ngx.send_headers()\n            ngx.say(ngx.var.foo)\n        ';\n        header_filter_by_lua '\n            ngx.var.foo = ndk.set_var.set_unescape_uri(\"a%20b\")\n        ';\n    }\n--- request\nGET /read\n--- response_body\na b\n\n\n\n=== TEST 8: log_by_lua\n--- config\n    location /read {\n        echo ok;\n        log_by_lua '\n            local foo = ndk.set_var.set_unescape_uri(\"a%20b\")\n            ngx.log(ngx.WARN, \"foo = \", foo)\n        ';\n    }\n--- request\nGET /read\n--- response_body\nok\n--- wait: 0.1\n--- error_log\nfoo = a b\n\n\n\n=== TEST 9: ngx.timer.*\n--- config\n    location /read {\n        echo ok;\n        log_by_lua '\n            ngx.timer.at(0, function ()\n                local foo = ndk.set_var.set_unescape_uri(\"a%20b\")\n                ngx.log(ngx.WARN, \"foo = \", foo)\n            end)\n        ';\n    }\n--- request\nGET /read\n--- response_body\nok\n--- wait: 0.1\n--- error_log\nfoo = a b\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/019-const.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => blocks() * repeat_each() * 2;\n\n#no_diff();\n#no_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /read {\n        content_by_lua '\n            ngx.say(ngx.OK)\n            ngx.say(ngx.AGAIN)\n            ngx.say(ngx.DONE)\n            ngx.say(ngx.ERROR)\n        ';\n    }\n--- request\nGET /read\n--- response_body\n0\n-2\n-4\n-1\n\n\n\n=== TEST 2: http constants\n--- config\n    location /read {\n        content_by_lua '\n            ngx.say(ngx.HTTP_GATEWAY_TIMEOUT)\n        ';\n    }\n--- request\nGET /read\n--- response_body\n504\n"
  },
  {
    "path": "t/020-subrequest.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse Test::Nginx::Util 'is_tcp_port_used';\n\n#master_on();\n#workers(1);\n#worker_connections(1014);\n#log_level('warn');\n#master_process_enabled(1);\n\nno_root_location;\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 23);\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n\n# NB: tcp_listen_port needs to be greater than 10000,\n# because the test cases expect it to be a 5-digit number\nmy $tcp_listen_port = 19113;\nwhile (++$tcp_listen_port < 65535) {\n    if (!is_tcp_port_used $tcp_listen_port) {\n        last;\n    }\n}\n$ENV{TEST_NGINX_TCP_LISTEN_PORT} = $tcp_listen_port;\n\n#no_diff();\nno_long_string();\n#no_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: DELETE\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_DELETE });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nDELETE\n--- error_log\nlua http subrequest \"/other?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: DELETE (proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_DELETE });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nDELETE\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: POST (nobody, proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /t {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_POST });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nPOST\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: HEAD\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_HEAD });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: explicit GET\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_GET });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nGET\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: implicit GET\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\")\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nGET\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: implicit GET (empty option table)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\", {})\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nGET\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: PUT (with body, proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo_read_request_body;\n\n        echo $echo_request_method;\n        echo_request_body;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body chomp\nPUT\nhello\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: PUT (with body, no proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        #echo_read_request_body;\n\n        echo $echo_request_method;\n        #echo $echo_request_body;\n        echo_request_body;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body chomp\nPUT\nhello\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: PUT (no body, no proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        #echo_read_request_body;\n\n        echo $echo_request_method;\n        #echo $echo_request_body;\n        echo_request_body;\n        #echo \"[$http_content_length]\";\n        echo;\n    }\n\n    location /foo {\n        echo $echo_request_method;\n        echo -n \"[$http_content_length]\";\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n\n            ngx.print(res.body)\n\n            res = ngx.location.capture(\"/foo\")\n            ngx.say(res.body)\n\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nPUT\nhello\nGET\n[]\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: POST (with body, proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo_read_request_body;\n\n        echo $echo_request_method;\n        echo_request_body;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_POST, body = \"hello\" });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body chomp\nPOST\nhello\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: POST (with body, memc method)\n--- config\n    location /flush {\n        set $memc_cmd flush_all;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /memc {\n        set $memc_key $echo_request_uri;\n        set $memc_exptime 600;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /lua {\n        content_by_lua '\n            ngx.location.capture(\"/flush\");\n\n            local res = ngx.location.capture(\"/memc\");\n            ngx.say(\"GET: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n            ngx.say(\"PUT: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\");\n            ngx.say(\"cached: \" .. res.body);\n\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nGET: 404\nPUT: 201\ncached: hello\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: POST (with body, memc method)\n--- config\n    location /flush {\n        set $memc_cmd flush_all;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /memc {\n        set $memc_cmd \"\";\n        set $memc_key $echo_request_uri;\n        set $memc_exptime 600;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /lua {\n        content_by_lua '\n            ngx.location.capture(\"/flush\",\n                { share_all_vars = true });\n\n            local res = ngx.location.capture(\"/memc\",\n                { share_all_vars = true });\n            ngx.say(\"GET: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello\", share_all_vars = true });\n            ngx.say(\"PUT: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\", { share_all_vars = true });\n            ngx.say(\"cached: \" .. res.body);\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nGET: 404\nPUT: 201\ncached: hello\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: empty args option table\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = {} })\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body eval: \"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: non-empty args option table (1 pair)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { [\"fo=\"] = \"=>\" } })\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nfo%3D=%3D%3E\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: non-empty args option table (2 pairs)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { [\"fo=\"] = \"=>\",\n                    [\"=\"] = \":\" } })\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like chop\n^(?:fo%3D=%3D%3E\\&%3D=%3A|%3D=%3A\\&fo%3D=%3D%3E)$\n--- no_error_log\n[error]\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: non-empty args option table (2 pairs, no special chars)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { foo = 3,\n                    bar = \"hello\" } })\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like chop\n^(?:bar=hello\\&foo=3|foo=3\\&bar=hello)$\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: non-empty args option table (number key)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { [57] = \"hi\" } })\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nattempt to use a non-string key in the \"args\" option table\n\n\n\n=== TEST 19: non-empty args option table (plain arrays)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { \"hi\" } })\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nattempt to use a non-string key in the \"args\" option table\n\n\n\n=== TEST 20: more args\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo?a=3\",\n                { args = { b = 4 } })\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\na=3&b=4\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: more args\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo?a=3\",\n                { args = \"b=4\" })\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\na=3&b=4\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: is_subrequest in main request\n--- config\n    location /lua {\n        content_by_lua '\n            if ngx.is_subrequest then\n                ngx.say(\"sub req\")\n            else\n                ngx.say(\"main req\")\n            end\n        ';\n    }\n--- request\n    GET /lua\n--- response_body\nmain req\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: is_subrequest in sub request\n--- config\n    location /main {\n        echo_location /lua;\n    }\n\n    location /lua {\n        content_by_lua '\n            if ngx.is_subrequest then\n                ngx.say(\"sub req\")\n            else\n                ngx.say(\"main req\")\n            end\n        ';\n    }\n--- request\n    GET /main\n--- response_body\nsub req\n--- no_error_log\n[error]\n\n\n\n=== TEST 24: is_subrequest in sub request in set_by_lua\n--- config\n    location /main {\n        echo_location /lua;\n    }\n\n    location /lua {\n        set_by_lua $a '\n            if ngx.is_subrequest then\n                return \"sub req\"\n            else\n                return \"main req\"\n            end\n        ';\n        echo $a;\n    }\n--- request\n    GET /main\n--- response_body\nsub req\n--- no_error_log\n[error]\n\n\n\n=== TEST 25: header inheritance bug (without body) (github issue 38)\nhttps://github.com/chaoslawful/lua-nginx-module/issues/38\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo -n $http_foo;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_GET });\n            ngx.say(\"header foo: [\", res.body, \"]\")\n        ';\n    }\n--- request\nGET /lua\n--- more_headers\nFoo: bar\n--- response_body\nheader foo: [bar]\n--- no_error_log\n[error]\n\n\n\n=== TEST 26: header inheritance bug (with body) (github issue 38)\nhttps://github.com/chaoslawful/lua-nginx-module/issues/38\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo -n $http_foo;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { body = \"abc\" });\n            ngx.say(\"header foo: [\", res.body, \"]\")\n        ';\n    }\n--- request\nGET /lua\n--- more_headers\nFoo: bar\n--- response_body\nheader foo: [bar]\n--- no_error_log\n[error]\n\n\n\n=== TEST 27: lua calls lua via subrequests\n--- config\n    location /a {\n        content_by_lua '\n            ngx.say(\"hello, a\");\n        ';\n    }\n    location /b {\n        content_by_lua '\n            ngx.say(\"hello, b\");\n        ';\n    }\n    location /c {\n        content_by_lua '\n            ngx.say(\"hello, c\");\n        ';\n    }\n    location /main {\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi({{\"/a\"}, {\"/b\"}})\n            local res3 = ngx.location.capture(\"/c\")\n            ngx.print(res1.body, res2.body, res3.body)\n        ';\n    }\n--- request\n    GET /main\n--- response_body\nhello, a\nhello, b\nhello, c\n--- error_log\nlua reuse free buf memory\n--- no_error_log\n[error]\n\n\n\n=== TEST 28: POST (with body, proxy method, main request is a POST too)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo_read_request_body;\n\n        echo $echo_request_method;\n        echo_request_body;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_POST, body = \"hello\" });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nPOST /lua\nhi\n--- response_body chomp\nPOST\nhello\n--- no_error_log\n[error]\n\n\n\n=== TEST 29: Last-Modified response header for static file subrequest\n--- config\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo.html\")\n\n            ngx.say(res.status)\n            ngx.say(res.header[\"Last-Modified\"])\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- user_files\n>>> foo.html\nhello, static file\n--- response_body_like chomp\n^200\n[A-Za-z]+, \\d{1,2} [A-Za-z]+ \\d{4} \\d{2}:\\d{2}:\\d{2} GMT\nhello, static file$\n--- no_error_log\n[error]\n\n\n\n=== TEST 30: custom ctx table for subrequest\n--- config\n    location /sub {\n        content_by_lua '\n            ngx.ctx.foo = \"bar\";\n        ';\n    }\n    location /lua {\n        content_by_lua '\n            local ctx = {}\n            local res = ngx.location.capture(\"/sub\", { ctx = ctx })\n\n            ngx.say(ctx.foo);\n            ngx.say(ngx.ctx.foo);\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nbar\nnil\n--- no_error_log\n[error]\n\n\n\n=== TEST 31: share the ctx with the parent\n--- config\n    location /sub {\n        content_by_lua '\n            ngx.ctx.foo = \"bar\";\n        ';\n    }\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\", { ctx = ngx.ctx })\n            ngx.say(ngx.ctx.foo);\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nbar\n--- no_error_log\n[error]\n\n\n\n=== TEST 32: test memcached with subrequests\n--- http_config\n    upstream memc {\n        server 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n        keepalive 100;\n    }\n--- config\n    location /memc {\n        set $memc_key some_key;\n        set $memc_exptime 600;\n        memc_pass memc;\n    }\n\n    location /t {\n        content_by_lua '\n            local res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello 1234\" });\n            -- ngx.say(\"PUT: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\");\n            ngx.say(\"some_key: \" .. res.body);\n        ';\n    }\n--- request\nGET /t\n--- response_body\nsome_key: hello 1234\n--- error_log\nlua reuse free buf chain, but reallocate memory because\n--- no_error_log\n[error]\n\n\n\n=== TEST 33: main POST, sub GET (main does not read the body)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_method)\n            ngx.say(ngx.req.get_body_data())\n        ';\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n        #proxy_pass http://127.0.0.1:8892/other;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_GET });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nPOST /lua\nhello, world\n--- response_body\nGET\nnil\n--- no_error_log\n[error]\n\n\n\n=== TEST 34: main POST, sub GET (main has read the body)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_method)\n            ngx.say(ngx.req.get_body_data())\n        ';\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n        #proxy_pass http://127.0.0.1:8892/other;\n    }\n\n    location /lua {\n        content_by_lua '\n            ngx.req.read_body()\n\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_GET });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nPOST /lua\nhello, world\n--- response_body\nGET\nnil\n--- no_error_log\n[error]\n\n\n\n=== TEST 35: main POST, sub POST (inherit bodies directly)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_method)\n            ngx.say(ngx.req.get_body_data())\n        ';\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n        #proxy_pass http://127.0.0.1:8892/other;\n    }\n\n    location /lua {\n        content_by_lua '\n            ngx.req.read_body()\n\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_POST });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nPOST /lua\nhello, world\n--- response_body\nPOST\nhello, world\n--- no_error_log\n[error]\n\n\n\n=== TEST 36: main POST, sub PUT (inherit bodies directly)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_method)\n            ngx.say(ngx.req.get_body_data())\n        ';\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n        #proxy_pass http://127.0.0.1:8892/other;\n    }\n\n    location /lua {\n        content_by_lua '\n            ngx.req.read_body()\n\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_PUT });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nPOST /lua\nhello, world\n--- response_body\nPUT\nhello, world\n--- no_error_log\n[error]\n\n\n\n=== TEST 37: recursive calls\n--- config\n    location /t {\n        content_by_lua '\n            ngx.location.capture(\"/t\")\n        ';\n    }\n--- request\n    GET /t\n--- ignore_response\n--- error_log\nlua subrequests cycle while processing \"/t\"\n\n\n\n=== TEST 38: OPTIONS\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_OPTIONS });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nOPTIONS\n--- no_error_log\n[error]\n\n\n\n=== TEST 39: OPTIONS with a body\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n        echo_request_body;\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_OPTIONS, body = \"hello world\" });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body chop\nOPTIONS\nhello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 40: encode args table with a multi-value arg.\n--- config\n    location /t {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args()\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local res = ngx.location.capture(\"/sub\", { args = args })\n            ngx.print(res.body)\n        ';\n    }\n\n    location /sub {\n        echo $query_string;\n    }\n--- request\nGET /t?r[]=http%3A%2F%2Fajax.googleapis.com%3A80%2Fajax%2Flibs%2Fjquery%2F1.7.2%2Fjquery.min.js&r[]=http%3A%2F%2Fajax.googleapis.com%3A80%2Fajax%2Flibs%2Fdojo%2F1.7.2%2Fdojo%2Fdojo.js.uncompressed.js\n--- response_body\nr%5B%5D=http%3A%2F%2Fajax.googleapis.com%3A80%2Fajax%2Flibs%2Fjquery%2F1.7.2%2Fjquery.min.js&r%5B%5D=http%3A%2F%2Fajax.googleapis.com%3A80%2Fajax%2Flibs%2Fdojo%2F1.7.2%2Fdojo%2Fdojo.js.uncompressed.js\n--- no_error_log\n[error]\n\n\n\n=== TEST 41: subrequests finalized with NGX_ERROR\n--- config\n    location /sub {\n        content_by_lua '\n            ngx.exit(ngx.ERROR)\n        ';\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n        ';\n    }\n--- request\nGET /main\n--- response_body\nstatus: 500\nbody: \n\n\n\n=== TEST 42: subrequests finalized with 500\n--- config\n    location /sub {\n        return 500;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n        ';\n    }\n--- request\nGET /main\n--- response_body\nstatus: 500\nbody: \n\n\n\n=== TEST 43: subrequests with an output body filter returning NGX_ERROR\n--- no_http2\n--- config\n    location /sub {\n        echo hello world;\n        body_filter_by_lua '\n            return ngx.ERROR\n        ';\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n        ';\n    }\n--- request\nGET /main\n--- stap2\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n--- response_body\n--- error_code\n--- no_error_log\n[error]\n--- curl_error eval\nqr{(\\Qcurl: (52) Empty reply from server\\E|\\Qcurl: (95) HTTP/3 stream 0 reset by server\\E)}ms\n\n\n\n=== TEST 44: subrequests truncated in its response body due to premature connection close (nonbuffered)\n--- config\n    server_tokens off;\n    location /memc {\n        internal;\n\n        set $memc_key 'foo';\n        #set $memc_exptime 300;\n        memc_pass 127.0.0.1:$TEST_NGINX_RAND_PORT_1; #$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/memc\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_RAND_PORT_1\n--- tcp_query_len: 9\n--- tcp_reply eval\n\"VALUE foo 0 1024\\r\\nhello world\"\n\n--- stap2\nF(ngx_http_lua_capture_body_filter) {\n    if (pid() == target() && $r != $r->main) {\n        printf(\"lua capture body output: %s\\n\", ngx_chain_dump($in))\n        if ($in->buf->last_in_chain) {\n            print_ubacktrace()\n        }\n    }\n}\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nupstream fin req: error=0 eof=1 rc=502\npost subreq: rc=0, status=200\n\n--- response_body\nstatus: 200\nbody: hello world\ntruncated: true\n--- error_log\nupstream prematurely closed connection\n\n\n\n=== TEST 45: subrequests truncated in its response body due to upstream read timeout (nonbuffered)\n--- config\n    memc_read_timeout 100ms;\n    location /memc {\n        internal;\n\n        set $memc_key 'foo';\n        #set $memc_exptime 300;\n        memc_pass 127.0.0.1:$TEST_NGINX_RAND_PORT_1; #$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/memc\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_RAND_PORT_1\n--- tcp_no_close\n--- tcp_reply eval\n\"VALUE foo 0 1024\\r\\nhello world\"\n\n--- stap2\nF(ngx_http_lua_capture_body_filter) {\n    if (pid() == target() && $r != $r->main) {\n        printf(\"lua capture body output: %s\\n\", ngx_chain_dump($in))\n        //if ($in->buf->last_in_chain) {\n            print_ubacktrace()\n        //}\n    }\n}\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nconn err: 110: upstream timed out\nupstream fin req: error=0 eof=0 rc=504\npost subreq: rc=0, status=200\n\n--- response_body_like chop\n^status: 200\nbody: [^\\n]*\ntruncated: true\n\n--- error_log\nupstream timed out\n\n\n\n=== TEST 46: subrequests truncated in its response body due to premature connection close (buffered)\n--- config\n    server_tokens off;\n\n    location /proxy {\n        internal;\n\n        #proxy_read_timeout 100ms;\n        proxy_buffering on;\n        proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT\n--- tcp_query_len: 65\n--- tcp_reply eval\n\"HTTP/1.0 200 OK\\r\\nContent-Length: 1024\\r\\n\\r\\nhello world\"\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nupstream fin req: error=0 eof=1 rc=502\npost subreq: rc=0, status=200\n\n--- response_body\nstatus: 200\nbody: hello world\ntruncated: true\n\n--- error_log\nupstream prematurely closed connection\n\n\n\n=== TEST 47: subrequests truncated in its response body due to read timeout (buffered)\n--- config\n    location /proxy {\n        internal;\n\n        proxy_read_timeout 100ms;\n        proxy_buffering on;\n        proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT\n--- tcp_no_close\n--- tcp_reply eval\n\"HTTP/1.0 200 OK\\r\\nContent-Length: 1024\\r\\n\\r\\nhello world\"\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nconn err: 110: upstream timed out\nupstream fin req: error=0 eof=0 rc=502\npost subreq: rc=0, status=200\n\n--- response_body\nstatus: 200\nbody: \ntruncated: true\n\n--- error_log\nupstream timed out\n\n\n\n=== TEST 48: subrequests truncated in its response body due to premature connection close (buffered, no content-length)\n--- config\n    server_tokens off;\n    location /proxy {\n        internal;\n\n        #proxy_read_timeout 100ms;\n        proxy_buffering on;\n        proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT\n--- tcp_query_len: 65\n--- tcp_reply eval\n\"HTTP/1.0 200 OK\\r\\n\\r\\nhello world\"\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nupstream fin req: error=0 eof=1 rc=0\npost subreq: rc=0, status=200\n\n--- response_body\nstatus: 200\nbody: hello world\ntruncated: false\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 49: subrequests truncated in its response body due to read timeout (buffered, no content-length)\n--- config\n    location /proxy {\n        internal;\n\n        proxy_read_timeout 100ms;\n        proxy_buffering on;\n        proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT\n--- tcp_no_close\n--- tcp_reply eval\n\"HTTP/1.0 200 OK\\r\\n\\r\\nhello world\"\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nconn err: 110: upstream timed out\nupstream fin req: error=0 eof=0 rc=502\npost subreq: rc=0, status=200\n\n--- response_body\nstatus: 200\nbody: \ntruncated: true\n\n--- error_log\nupstream timed out\n\n\n\n=== TEST 50: subrequests truncated in its response body due to premature connection close (nonbuffered, no content-length)\n--- config\n    server_tokens off;\n\n    location /proxy {\n        internal;\n\n        #proxy_read_timeout 100ms;\n        proxy_buffering off;\n        proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT\n--- tcp_query_len: 65\n--- tcp_reply eval\n\"HTTP/1.0 200 OK\\r\\n\\r\\nhello world\"\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nupstream fin req: error=0 eof=1 rc=0\npost subreq: rc=0, status=200\n\n--- response_body\nstatus: 200\nbody: hello world\ntruncated: false\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 51: subrequests truncated in its response body due to read timeout (nonbuffered, no content-length)\n--- config\n    location /proxy {\n        internal;\n\n        proxy_read_timeout 500ms;\n        proxy_buffering off;\n        proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT\n--- tcp_no_close\n--- tcp_reply eval\n\"HTTP/1.0 200 OK\\r\\n\\r\\nhello world\"\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nconn err: 110: upstream timed out\nupstream fin req: error=0 eof=0 rc=504\npost subreq: rc=0, status=200\n\n--- response_body\nstatus: 200\nbody: hello world\ntruncated: true\n\n--- error_log\nupstream timed out\n\n\n\n=== TEST 52: forwarding in-memory request bodies to multiple subrequests\n--- config\n    location /other {\n        default_type 'foo/bar';\n        proxy_pass http://127.0.0.1:$server_port/back;\n    }\n\n    location /back {\n        echo_read_request_body;\n        echo_request_body;\n    }\n\n    location /lua {\n        content_by_lua '\n            ngx.req.read_body()\n\n            for i = 1, 2 do\n                local res = ngx.location.capture(\"/other\",\n                    { method = ngx.HTTP_POST });\n\n                ngx.say(res.body)\n            end\n        ';\n    }\n\n--- request eval\n\"POST /lua\n\" . \"hello world\"\n\n--- response_body\nhello world\nhello world\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 53: forwarding in-file request bodies to multiple subrequests (client_body_in_file_only)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        proxy_pass http://127.0.0.1:$server_port/back;\n    }\n\n    location /back {\n        echo_read_request_body;\n        echo_request_body;\n    }\n\n    client_body_in_file_only on;\n\n    location /lua {\n        content_by_lua '\n            ngx.req.read_body()\n\n            for i = 1, 2 do\n                local res = ngx.location.capture(\"/other\",\n                    { method = ngx.HTTP_POST });\n\n                ngx.say(res.body)\n            end\n        ';\n    }\n\n--- request eval\n\"POST /lua\n\" . \"hello world\"\n\n--- response_body\nhello world\nhello world\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 54: forwarding in-file request bodies to multiple subrequests (exceeding client_body_buffer_size)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        proxy_pass http://127.0.0.1:$server_port/back;\n    }\n\n    location /back {\n        echo_read_request_body;\n        echo_request_body;\n    }\n\n    location /lua {\n        #client_body_in_file_only on;\n        client_body_buffer_size 1;\n        content_by_lua '\n            ngx.req.read_body()\n\n            for i = 1, 2 do\n                local res = ngx.location.capture(\"/other\",\n                    { method = ngx.HTTP_POST });\n\n                ngx.say(res.body)\n            end\n        ';\n    }\n--- request eval\n\"POST /lua\n\" . (\"hello world\" x 100)\n\n--- stap2\nglobal valid = 0\nglobal fds\n\nF(ngx_http_handler) { valid = 1  }\n\nprobe syscall.open {\n    if (valid && pid() == target()) {\n        print(name, \"(\", argstr, \")\")\n    }\n}\n\nprobe syscall.close {\n    if (valid && pid() == target() && fds[sprintf(\"%d\", $fd)]) {\n        println(name, \"(\", argstr, \")\")\n    }\n}\n\nprobe syscall.unlink {\n    if (valid && pid() == target()) {\n        println(name, \"(\", argstr, \")\")\n    }\n}\n\nprobe syscall.open.return {\n    if (valid && pid() == target()) {\n        println(\" = \", retstr)\n        fds[retstr] = 1\n    }\n}\n\nF(ngx_http_lua_subrequest) {\n    println(\"lua subrequest\")\n}\n\nF(ngx_output_chain) {\n    printf(\"output chain: %s\\n\", ngx_chain_dump($in))\n}\n\nF(ngx_pool_run_cleanup_file) {\n    println(\"clean up file: \", $fd)\n}\n\n--- response_body eval\n(\"hello world\" x 100) . \"\\n\"\n. (\"hello world\" x 100) . \"\\n\"\n\n--- no_error_log\n[error]\n--- error_log\na client request body is buffered to a temporary file\n\n\n\n=== TEST 55: subrequests truncated in its response body due to premature connection close (buffered + chunked)\n--- config\n    server_tokens off;\n\n    location /proxy {\n        internal;\n\n        #proxy_read_timeout 100ms;\n        proxy_http_version 1.1;\n        proxy_buffering on;\n        proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT\n--- tcp_query_len: 65\n--- tcp_reply eval\n\"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\nb\\r\\nhello world\\r\"\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nupstream fin req: error=0 eof=1 rc=502\npost subreq: rc=0, status=200\n\n--- response_body\nstatus: 200\nbody: hello world\ntruncated: true\n\n--- error_log\nupstream prematurely closed connection\n\n\n\n=== TEST 56: subrequests truncated in its response body due to premature connection close (nonbuffered + chunked)\n--- config\n    server_tokens off;\n\n    location /proxy {\n        internal;\n\n        #proxy_read_timeout 100ms;\n        proxy_http_version 1.1;\n        proxy_buffering off;\n        proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT\n--- tcp_query_len: 65\n--- tcp_reply eval\n\"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\nb\\r\\nhello world\\r\"\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nupstream fin req: error=0 eof=1 rc=502\npost subreq: rc=0, status=200\n\n--- response_body\nstatus: 200\nbody: hello world\ntruncated: true\n\n--- error_log\nupstream prematurely closed connection\n\n\n\n=== TEST 57: subrequests truncated in its response body due to read timeout (buffered + chunked)\n--- config\n    location /proxy {\n        internal;\n\n        proxy_read_timeout 100ms;\n        proxy_buffering on;\n        proxy_http_version 1.1;\n        proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT\n--- tcp_no_close\n--- tcp_reply eval\n\"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\nb\\r\\nhello world\\r\"\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nconn err: 110: upstream timed out\nupstream fin req: error=0 eof=0 rc=502\npost subreq: rc=0, status=200\n\n--- response_body\nstatus: 200\nbody: \ntruncated: true\n\n--- error_log\nupstream timed out\n\n\n\n=== TEST 58: good chunked response (buffered)\n--- config\n    location /proxy {\n        internal;\n\n        #proxy_read_timeout 100ms;\n        proxy_buffering on;\n        proxy_http_version 1.1;\n        proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT\n--- tcp_no_close\n--- tcp_reply eval\n\"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5\\r\\nhello\\r\\n0\\r\\n\\r\\n\"\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nupstream fin req: error=0 eof=0 rc=0\npost subreq: rc=0, status=200\n\n--- response_body\nstatus: 200\nbody: hello\ntruncated: false\n\n\n\n=== TEST 59: good chunked response (nonbuffered)\n--- config\n    location /proxy {\n        internal;\n\n        #proxy_read_timeout 100ms;\n        proxy_buffering off;\n        proxy_http_version 1.1;\n        proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT\n--- tcp_no_close\n--- tcp_reply eval\n\"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5\\r\\nhello\\r\\n0\\r\\n\\r\\n\"\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nupstream fin req: error=0 eof=0 rc=0\npost subreq: rc=0, status=200\n\n--- response_body\nstatus: 200\nbody: hello\ntruncated: false\n\n\n\n=== TEST 60: subrequests truncated in its response body due to premature connection close (nonbuffered + proxy)\n--- config\n    server_tokens off;\n\n    location /proxy {\n        internal;\n\n        #proxy_read_timeout 100ms;\n        proxy_buffering off;\n        proxy_pass http://127.0.0.1:$TEST_NGINX_TCP_LISTEN_PORT;\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.say(\"status: \", res.status)\n            ngx.say(\"body: \", res.body)\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /main\n--- tcp_listen: $TEST_NGINX_TCP_LISTEN_PORT\n--- tcp_query_len: 65\n--- tcp_reply eval\n\"HTTP/1.0 200 OK\\r\\nContent-Length: 1024\\r\\n\\r\\nhello world\"\n\n--- stap\nF(ngx_http_upstream_finalize_request) {\n    printf(\"upstream fin req: error=%d eof=%d rc=%d\\n\",\n        $r->upstream->peer->connection->read->error,\n        $r->upstream->peer->connection->read->eof,\n        $rc)\n    #print_ubacktrace()\n}\nF(ngx_connection_error) {\n    printf(\"conn err: %d: %s\\n\", $err, user_string($text))\n    #print_ubacktrace()\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: rc=%d, status=%d\\n\", $rc, $r->headers_out->status)\n    #print_ubacktrace()\n}\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize: %d\\n\", $rc)\n}\n*/\n--- stap_out\nupstream fin req: error=0 eof=1 rc=502\npost subreq: rc=0, status=200\n\n--- response_body\nstatus: 200\nbody: hello world\ntruncated: true\n\n--- error_log\nupstream prematurely closed connection\n\n\n\n=== TEST 61: WebDAV methods\n--- config\n    location /other {\n        echo \"method: $echo_request_method\";\n    }\n\n    location /lua {\n        content_by_lua '\n            local methods = {\n                ngx.HTTP_MKCOL,\n                ngx.HTTP_COPY,\n                ngx.HTTP_MOVE,\n                ngx.HTTP_PROPFIND,\n                ngx.HTTP_PROPPATCH,\n                ngx.HTTP_LOCK,\n                ngx.HTTP_UNLOCK,\n                ngx.HTTP_PATCH,\n                ngx.HTTP_TRACE,\n            }\n\n            for i, method in ipairs(methods) do\n                local res = ngx.location.capture(\"/other\",\n                    { method = method })\n                ngx.print(res.body)\n            end\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nmethod: MKCOL\nmethod: COPY\nmethod: MOVE\nmethod: PROPFIND\nmethod: PROPPATCH\nmethod: LOCK\nmethod: UNLOCK\nmethod: PATCH\nmethod: TRACE\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 62: by default DELETE subrequests don't forward request bodies\n--- config\n    location /other {\n        default_type 'foo/bar';\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.req.get_body_data())\n        ';\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_DELETE });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nDELETE /lua\nhello world\n--- response_body\nnil\n--- no_error_log\n[error]\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 63: DELETE subrequests do forward request bodies when always_forward_body == true\n--- config\n    location = /other {\n        default_type 'foo/bar';\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.req.get_body_data())\n        ';\n    }\n\n    location /lua {\n        content_by_lua '\n            ngx.req.read_body()\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_DELETE, always_forward_body = true });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nDELETE /lua\nhello world\n--- response_body\nhello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 64: DELETE subrequests do forward request bodies when always_forward_body == true (on disk)\n--- config\n    location = /other {\n        default_type 'foo/bar';\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.req.get_body_data())\n        ';\n    }\n\n    location /lua {\n        content_by_lua '\n            ngx.req.read_body()\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_DELETE, always_forward_body = true });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nDELETE /lua\nhello world\n--- stap2\nglobal c\nprobe process(\"$LIBLUA_PATH\").function(\"rehashtab\") {\n    c++\n    //print_ubacktrace()\n    printf(\"rehash: %d\\n\", c)\n}\n--- stap_out2\n--- response_body\nhello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 65: DELETE\n--- config\n    location = /t {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n        ';\n    }\n    location = /sub {\n        echo hello;\n        echo world;\n    }\n--- request\nGET /t\n--- response_body\nhello\nworld\n--- stap\nF(ngx_http_lua_capture_header_filter) {\n    println(\"capture header filter\")\n}\n\nF(ngx_http_lua_capture_body_filter) {\n    println(\"capture body filter\")\n}\n\n--- stap_out\ncapture header filter\ncapture body filter\ncapture body filter\ncapture body filter\ncapture header filter\ncapture body filter\ncapture body filter\n--- no_error_log\n[error]\n\n\n\n=== TEST 66: leafo test case 1 for assertion failures\n--- config\n    location = /t {\n        echo hello;\n    }\n\n    location /proxy {\n        internal;\n        rewrite_by_lua \"\n          local req = ngx.req\n          print(ngx.var._url)\n\n          for k,v in pairs(req.get_headers()) do\n            if k ~= 'content-length' then\n              req.clear_header(k)\n            end\n          end\n\n          if ngx.ctx.headers then\n            for k,v in pairs(ngx.ctx.headers) do\n              req.set_header(k, v)\n            end\n          end\n        \";\n\n        proxy_http_version 1.1;\n        proxy_pass $_url;\n    }\n\n    location /first {\n      set $_url \"\";\n      content_by_lua '\n        local res = ngx.location.capture(\"/proxy\", {\n          ctx = {\n            headers = {\n              [\"Content-type\"] = \"application/x-www-form-urlencoded\"\n            }\n          },\n          vars = { _url = \"http://127.0.0.1:\" .. ngx.var.server_port .. \"/t\" }\n        })\n\n        ngx.print(res.body)\n\n        local res = ngx.location.capture(\"/proxy\", {\n          ctx = {\n            headers = {\n              [\"x-some-date\"] = \"Sun, 01 Dec 2013 11:47:41 GMT\",\n              [\"x-hello-world-header\"] = \"123412341234\",\n              [\"Authorization\"] = \"Hello\"\n            }\n          },\n          vars = { _url = \"http://127.0.0.1:\" .. ngx.var.server_port .. \"/t\" }\n        })\n\n        ngx.print(res.body)\n      ';\n    }\n--- request\nGET /first\n--- response_body\nhello\nhello\n--- no_error_log eval\n[\n\"[error]\",\nqr/Assertion .*? failed/\n]\n\n\n\n=== TEST 67: leafo test case 2 for assertion failures\n--- config\n    location = /t {\n        echo hello;\n    }\n\n    location /proxy {\n        internal;\n        rewrite_by_lua \"\n          local req = ngx.req\n          print(ngx.var._url)\n\n          for k,v in pairs(req.get_headers()) do\n            if k ~= 'content-length' then\n              req.clear_header(k)\n            end\n          end\n\n          if ngx.ctx.headers then\n            for k,v in pairs(ngx.ctx.headers) do\n              req.set_header(k, v)\n            end\n          end\n        \";\n\n        proxy_http_version 1.1;\n        proxy_pass $_url;\n    }\n\n    location /second {\n      set $_url \"\";\n      content_by_lua '\n        local res = ngx.location.capture(\"/proxy\", {\n          method = ngx.HTTP_POST,\n          body = (\"x\"):rep(600),\n          ctx = {\n            headers = {\n              [\"Content-type\"] = \"application/x-www-form-urlencoded\"\n            }\n          },\n          vars = { _url = \"http://127.0.0.1:\" .. ngx.var.server_port .. \"/t\" }\n        })\n\n        ngx.print(res.body)\n\n        local res = ngx.location.capture(\"/proxy\", {\n          ctx = {\n            headers = {\n              [\"x-some-date\"] = \"Sun, 01 Dec 2013 11:47:41 GMT\",\n              [\"x-hello-world-header\"] = \"123412341234\",\n              [\"Authorization\"] = \"Hello\"\n            }\n          },\n          vars = { _url = \"http://127.0.0.1:\" .. ngx.var.server_port .. \"/t\" }\n        })\n\n        ngx.print(res.body)\n\n        local res = ngx.location.capture(\"/proxy\", {\n          vars = { _url = \"http://127.0.0.1:\" .. ngx.var.server_port .. \"/t\" }\n        })\n\n        ngx.print(res.body)\n      ';\n    }\n--- request\nGET /second\n--- response_body\nhello\nhello\nhello\n--- no_error_log eval\n[\n\"[error]\",\nqr/Assertion .*? failed/\n]\n\n\n\n=== TEST 68: fetch subrequest's builtin request headers\n--- config\n    location = /sub {\n        echo \"sr: User-Agent: $http_user_agent\";\n        echo \"sr: Host: $http_host\";\n    }\n\n    location = /t {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n            ngx.say(\"pr: User-Agent: \", ngx.var.http_user_agent)\n            ngx.say(\"pr: Host: \", ngx.var.http_host)\n        ';\n    }\n--- request\n    GET /t\n--- more_headers\nUser-Agent: foo\n--- response_body\nsr: User-Agent: foo\nsr: Host: localhost\npr: User-Agent: foo\npr: Host: localhost\n\n--- no_error_log\n[error]\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 69: modify subrequest's builtin request headers\n--- config\n    location = /sub {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", \"bar\")\n        ';\n        echo \"sr: User-Agent: $http_user_agent\";\n        echo \"sr: Host: $http_host\";\n    }\n\n    location = /t {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n            ngx.say(\"pr: User-Agent: \", ngx.var.http_user_agent)\n            ngx.say(\"pr: Host: \", ngx.var.http_host)\n        ';\n    }\n--- request\n    GET /t\n--- more_headers\nUser-Agent: foo\n--- response_body\nsr: User-Agent: bar\nsr: Host: localhost\npr: User-Agent: foo\npr: Host: localhost\n\n--- no_error_log\n[error]\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 70: modify subrequest's builtin request headers (main req is POST)\n--- config\n    location = /sub {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", \"bar\")\n        ';\n        echo \"sr: User-Agent: $http_user_agent\";\n        echo \"sr: Host: $http_host\";\n    }\n\n    location = /t {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n            ngx.say(\"pr: User-Agent: \", ngx.var.http_user_agent)\n            ngx.say(\"pr: Host: \", ngx.var.http_host)\n        ';\n    }\n--- request\nPOST /t\nhello world\n--- more_headers\nUser-Agent: foo\n--- response_body\nsr: User-Agent: bar\nsr: Host: localhost\npr: User-Agent: foo\npr: Host: localhost\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 71: duplicate request headers (main req is POST)\n--- config\n    location = /sub {\n        echo \"sr: Cookie: $http_cookie\";\n    }\n\n    location = /t {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n            ngx.say(\"pr: Cookie: \", ngx.var.http_cookie)\n        ';\n    }\n--- request\nPOST /t\nhello world\n--- more_headers\nCookie: foo\nCookie: bar\n--- response_body\nsr: Cookie: foo; bar\npr: Cookie: foo; bar\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 72: duplicate request headers (main req is GET)\n--- config\n    location = /sub {\n        echo \"sr: Cookie: $http_cookie\";\n    }\n\n    location = /t {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n            ngx.say(\"pr: Cookie: \", ngx.var.http_cookie)\n        ';\n    }\n--- request\nGET /t\n--- more_headers\nCookie: foo\nCookie: bar\n--- response_body\nsr: Cookie: foo; bar\npr: Cookie: foo; bar\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 73: HEAD subrequest (github #347)\n--- config\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/index.html\",\n                { method = ngx.HTTP_HEAD });\n            ngx.say(\"content-length: \", res.header[\"Content-Length\"])\n            ngx.say(\"body: [\", res.body, \"]\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like chop\n^content-length: \\d+\nbody: \\[\\]\n$\n--- no_error_log\n[error]\n\n\n\n=== TEST 74: image_filter + ngx.location.capture\nngx_http_image_filter_module's header filter intercepts\nthe header filter chain so the r->header_sent flag won't\nget set right after the header filter chain is first invoked.\n\n--- config\n\nlocation = /back {\n    empty_gif;\n}\n\nlocation = /t {\n    image_filter rotate 90;\n\n    content_by_lua '\n        local res = ngx.location.capture(\"/back\")\n        for k, v in pairs(res.header) do\n            ngx.header[k] = v\n        end\n        ngx.status = res.status\n        ngx.print(res.body)\n    ';\n}\n\n--- request\nGET /t\n--- response_body_like: .\n--- stap\nF(ngx_http_image_header_filter) {\n    println(\"image header filter\")\n}\n--- stap_out\nimage header filter\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 75: WebDAV + MOVE\n--- config\n    location = /t {\n        content_by_lua_block {\n            local file1 = \"/file1.txt\"\n            local file2 = \"/file2.txt\"\n            ngx.req.set_header( \"Destination\", file2 )\n            local res = ngx.location.capture(\n                file1, { method = ngx.HTTP_MOVE }\n            )\n\n            ngx.say(\n                \"MOVE \", file1, \" -> \", file2,\n                \", response status: \", res.status\n            )\n        }\n    }\n\n    location / {\n        dav_methods MOVE;\n    }\n\n--- user_files\n>>> file1.txt\nhello, world!\n\n--- request\nGET /t\n\n--- response_body\nMOVE /file1.txt -> /file2.txt, response status: 204\n\n--- no_error_log\n[error]\n--- error_code: 200\n\n\n\n=== TEST 76: WebDAV + DELETE\n--- config\n    location = /t {\n        content_by_lua_block {\n            local file = \"/file.txt\"\n            local res = ngx.location.capture(\n                file, { method = ngx.HTTP_DELETE }\n            )\n\n            ngx.say(\n                \"DELETE \", file,\n                \", response status: \", res.status\n            )\n        }\n    }\n\n    location / {\n        dav_methods DELETE;\n    }\n\n--- user_files\n>>> file.txt\nhello, world!\n\n--- request\nGET /t\n\n--- response_body\nDELETE /file.txt, response status: 204\n\n--- no_error_log\n[error]\n--- error_code: 200\n\n\n\n=== TEST 77: avoid request smuggling 1/4 (default capture + smuggle in header)\n--- http_config\n    upstream backend {\n        server unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        keepalive 32;\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n\n        location / {\n            content_by_lua_block {\n                ngx.say(\"method: \", ngx.var.request_method,\n                        \", uri: \", ngx.var.uri,\n                        \", X: \", ngx.var.http_x)\n            }\n        }\n    }\n--- config\n    location /proxy {\n        proxy_http_version 1.1;\n        proxy_set_header   Connection \"\";\n        proxy_pass         http://backend/foo;\n    }\n\n    location /capture {\n        server_tokens off;\n        more_clear_headers Date;\n\n        content_by_lua_block {\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.print(res.body)\n        }\n    }\n\n    location /t {\n        content_by_lua_block {\n            local req = [[\nGET /capture HTTP/1.1\nHost: test.com\nContent-Length: 37\nTransfer-Encoding: chunked\n\n0\n\nGET /capture HTTP/1.1\nHost: test.com\nX: GET /bar HTTP/1.0\n\n]]\n\n            local sock = ngx.socket.tcp()\n            sock:settimeout(1000)\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_SERVER_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send req: \", err)\n                return\n            end\n\n            ngx.say(\"req bytes: \", bytes)\n\n            local n_resp = 0\n\n            local reader = sock:receiveuntil(\"\\r\\n\")\n            while true do\n                local line, err = reader()\n                if line then\n                    ngx.say(line)\n                    if line == \"0\" then\n                        n_resp = n_resp + 1\n                    end\n\n                    if n_resp >= 2 then\n                        break\n                    end\n\n                else\n                    ngx.say(\"err: \", err)\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n--- request\nGET /t\n--- response_body\nreq bytes: 146\nHTTP/1.1 200 OK\nServer: nginx\nContent-Type: text/plain\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n1f\nmethod: GET, uri: /foo, X: nil\n\n0\n\nHTTP/1.1 200 OK\nServer: nginx\nContent-Type: text/plain\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n2d\nmethod: GET, uri: /foo, X: GET /bar HTTP/1.0\n\n0\n--- no_error_log\n[error]\n--- skip_nginx\n3: >= 1.21.1\n\n\n\n=== TEST 78: avoid request smuggling 2/4 (POST capture + smuggle in body)\n--- http_config\n    upstream backend {\n        server unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        keepalive 32;\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n\n        location / {\n            content_by_lua_block {\n                ngx.say(\"method: \", ngx.var.request_method,\n                        \", uri: \", ngx.var.uri)\n            }\n        }\n    }\n--- config\n    location /proxy {\n        proxy_http_version 1.1;\n        proxy_set_header   Connection \"\";\n        proxy_pass         http://backend/foo;\n    }\n\n    location /capture {\n        server_tokens off;\n        more_clear_headers Date;\n\n        content_by_lua_block {\n            ngx.req.read_body()\n            local res = ngx.location.capture(\"/proxy\", { method = ngx.HTTP_POST })\n            ngx.print(res.body)\n        }\n    }\n\n    location /t {\n        content_by_lua_block {\n            local req = [[\nGET /capture HTTP/1.1\nHost: test.com\nContent-Length: 57\nTransfer-Encoding: chunked\n\n0\n\nPOST /capture HTTP/1.1\nHost: test.com\nContent-Length: 60\n\nPOST /bar HTTP/1.1\nHost: test.com\nContent-Length: 5\n\nhello\n\n]]\n\n            local sock = ngx.socket.tcp()\n            sock:settimeout(1000)\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_SERVER_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send req: \", err)\n                return\n            end\n\n            ngx.say(\"req bytes: \", bytes)\n\n            local n_resp = 0\n\n            local reader = sock:receiveuntil(\"\\r\\n\")\n            while true do\n                local line, err = reader()\n                if line then\n                    ngx.say(line)\n                    if line == \"0\" then\n                        n_resp = n_resp + 1\n                    end\n\n                    if n_resp >= 2 then\n                        break\n                    end\n\n                else\n                    ngx.say(\"err: \", err)\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n--- request\nGET /t\n--- response_body\nreq bytes: 205\nHTTP/1.1 200 OK\nServer: nginx\nContent-Type: text/plain\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n18\nmethod: POST, uri: /foo\n\n0\n\nHTTP/1.1 200 OK\nServer: nginx\nContent-Type: text/plain\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n18\nmethod: POST, uri: /foo\n\n0\n--- no_error_log\n[error]\n--- skip_nginx\n3: >= 1.21.1\n\n\n\n=== TEST 79: avoid request smuggling 3/4 (POST capture w/ always_forward_body + smuggle in body)\n--- http_config\n    upstream backend {\n        server unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        keepalive 32;\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n\n        location / {\n            content_by_lua_block {\n                ngx.say(\"method: \", ngx.var.request_method,\n                        \", uri: \", ngx.var.uri)\n            }\n        }\n    }\n--- config\n    location /proxy {\n        proxy_http_version 1.1;\n        proxy_set_header   Connection \"\";\n        proxy_pass         http://backend/foo;\n    }\n\n    location /capture {\n        server_tokens off;\n        more_clear_headers Date;\n\n        content_by_lua_block {\n            ngx.req.read_body()\n            local res = ngx.location.capture(\"/proxy\", {\n                method = ngx.HTTP_POST,\n                always_forward_body = true\n            })\n            ngx.print(res.body)\n        }\n    }\n\n    location /t {\n        content_by_lua_block {\n            local req = [[\nGET /capture HTTP/1.1\nHost: test.com\nContent-Length: 57\nTransfer-Encoding: chunked\n\n0\n\nPOST /capture HTTP/1.1\nHost: test.com\nContent-Length: 60\n\nPOST /bar HTTP/1.1\nHost: test.com\nContent-Length: 5\n\nhello\n\n]]\n\n            local sock = ngx.socket.tcp()\n            sock:settimeout(1000)\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_SERVER_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send req: \", err)\n                return\n            end\n\n            ngx.say(\"req bytes: \", bytes)\n\n            local n_resp = 0\n\n            local reader = sock:receiveuntil(\"\\r\\n\")\n            while true do\n                local line, err = reader()\n                if line then\n                    ngx.say(line)\n                    if line == \"0\" then\n                        n_resp = n_resp + 1\n                    end\n\n                    if n_resp >= 2 then\n                        break\n                    end\n\n                else\n                    ngx.say(\"err: \", err)\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n--- request\nGET /t\n--- response_body\nreq bytes: 205\nHTTP/1.1 200 OK\nServer: nginx\nContent-Type: text/plain\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n18\nmethod: POST, uri: /foo\n\n0\n\nHTTP/1.1 200 OK\nServer: nginx\nContent-Type: text/plain\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n18\nmethod: POST, uri: /foo\n\n0\n--- no_error_log\n[error]\n--- skip_nginx\n3: >= 1.21.1\n\n\n\n=== TEST 80: avoid request smuggling 4/4 (POST capture w/ body + smuggle in body)\n--- http_config\n    upstream backend {\n        server unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        keepalive 32;\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n\n        location / {\n            content_by_lua_block {\n                ngx.say(\"method: \", ngx.var.request_method,\n                        \", uri: \", ngx.var.uri)\n            }\n        }\n    }\n--- config\n    location /proxy {\n        proxy_http_version 1.1;\n        proxy_set_header   Connection \"\";\n        proxy_pass         http://backend/foo;\n    }\n\n    location /capture {\n        server_tokens off;\n        more_clear_headers Date;\n\n        content_by_lua_block {\n            ngx.req.read_body()\n            local res = ngx.location.capture(\"/proxy\", {\n                method = ngx.HTTP_POST,\n                always_forward_body = true,\n                body = ngx.req.get_body_data()\n            })\n            ngx.print(res.body)\n        }\n    }\n\n    location /t {\n        content_by_lua_block {\n            local req = [[\nGET /capture HTTP/1.1\nHost: test.com\nContent-Length: 57\nTransfer-Encoding: chunked\n\n0\n\nPOST /capture HTTP/1.1\nHost: test.com\nContent-Length: 60\n\nPOST /bar HTTP/1.1\nHost: test.com\nContent-Length: 5\n\nhello\n\n]]\n\n            local sock = ngx.socket.tcp()\n            sock:settimeout(1000)\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_SERVER_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send req: \", err)\n                return\n            end\n\n            ngx.say(\"req bytes: \", bytes)\n\n            local n_resp = 0\n\n            local reader = sock:receiveuntil(\"\\r\\n\")\n            while true do\n                local line, err = reader()\n                if line then\n                    ngx.say(line)\n                    if line == \"0\" then\n                        n_resp = n_resp + 1\n                    end\n\n                    if n_resp >= 2 then\n                        break\n                    end\n\n                else\n                    ngx.say(\"err: \", err)\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n--- request\nGET /t\n--- response_body\nreq bytes: 205\nHTTP/1.1 200 OK\nServer: nginx\nContent-Type: text/plain\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n18\nmethod: POST, uri: /foo\n\n0\n\nHTTP/1.1 200 OK\nServer: nginx\nContent-Type: text/plain\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n18\nmethod: POST, uri: /foo\n\n0\n--- no_error_log\n[error]\n--- skip_nginx\n3: >= 1.21.1\n\n\n\n=== TEST 81: bad HTTP method\n--- config\n    location /other { }\n\n    location /lua {\n        content_by_lua_block {\n            local res = ngx.location.capture(\"/other\",\n                { method = 10240 });\n        }\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nunsupported HTTP method: 10240\n\n\n\n=== TEST 82: bad requests with both Content-Length and Transfer-Encoding (nginx >= 1.21.1)\n--- http_config\n    upstream backend {\n        server unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        keepalive 32;\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n\n        location / {\n            content_by_lua_block {\n                ngx.say(\"method: \", ngx.var.request_method,\n                        \", uri: \", ngx.var.uri,\n                        \", X: \", ngx.var.http_x)\n            }\n        }\n    }\n--- config\n    location /proxy {\n        proxy_http_version 1.1;\n        proxy_set_header   Connection \"\";\n        proxy_pass         http://backend/foo;\n    }\n\n    location /capture {\n        server_tokens off;\n        more_clear_headers Date;\n\n        content_by_lua_block {\n            local res = ngx.location.capture(\"/proxy\")\n            ngx.print(res.body)\n        }\n    }\n\n    location /t {\n        content_by_lua_block {\n            local req = [[\nGET /capture HTTP/1.1\nHost: test.com\nContent-Length: 37\nTransfer-Encoding: chunked\n\n0\n\nGET /capture HTTP/1.1\nHost: test.com\nX: GET /bar HTTP/1.0\n\n]]\n\n            local sock = ngx.socket.tcp()\n            sock:settimeout(1000)\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_SERVER_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send req: \", err)\n                return\n            end\n\n            ngx.say(\"req bytes: \", bytes)\n\n            local n_resp = 0\n\n            local reader = sock:receiveuntil(\"\\r\\n\")\n            while true do\n                local line, err = reader()\n                if line then\n                    ngx.say(line)\n                    if line == \"0\" then\n                        n_resp = n_resp + 1\n                    end\n\n                    if n_resp >= 2 then\n                        break\n                    end\n\n                else\n                    ngx.say(\"err: \", err)\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n--- request\nGET /t\n--- response_body_like\nreq bytes: 146\nHTTP/1.1 400 Bad Request\n--- no_error_log\n[error]\n--- skip_nginx\n3: < 1.21.1\n\n\n\n=== TEST 83: avoid request smuggling of HEAD req\n--- config\n    location /capture {\n        server_tokens off;\n        more_clear_headers Date;\n\n        content_by_lua_block {\n            ngx.say(\"Hello\")\n        }\n    }\n\n    location /t {\n        content_by_lua_block {\n            local req = [[\nHEAD /capture HTTP/1.1\nHost: test.com\nContent-Length: 63\n\nGET /capture HTTP/1.1\nHost: test.com\nX: GET /bar HTTP/1.0\n\n]]\n\n            local sock = ngx.socket.tcp()\n            sock:settimeout(1000)\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_SERVER_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send req: \", err)\n                return\n            end\n\n            ngx.say(\"req bytes: \", bytes)\n\n            local n_resp = 0\n\n            local reader = sock:receiveuntil(\"\\r\\n\")\n            while true do\n                local line, err = reader()\n                if line then\n                    ngx.say(line)\n                    if line == \"0\" then\n                        n_resp = n_resp + 1\n                    end\n\n                    if n_resp >= 2 then\n                        break\n                    end\n\n                else\n                    ngx.say(\"err: \", err)\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n--- request\nGET /t\n--- response_body\nreq bytes: 117\nHTTP/1.1 200 OK\nServer: nginx\nContent-Type: text/plain\nConnection: keep-alive\n\nerr: timeout\n--- error_log\nlua tcp socket read timed out\n"
  },
  {
    "path": "t/021-cookie-time.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 2);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: cookie_time\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(ngx.cookie_time(1290079655))\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nThu, 18-Nov-10 11:27:35 GMT\n\n\n\n=== TEST 2: cookie_time in set_by_lua\n--- config\n    location /lua {\n        set_by_lua $a '\n            return ngx.cookie_time(1290079655)\n        ';\n        echo $a;\n    }\n--- request\nGET /lua\n--- response_body\nThu, 18-Nov-10 11:27:35 GMT\n"
  },
  {
    "path": "t/022-redirect.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3 + 9);\n\n#no_diff();\n#no_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: default 302\n--- config\n    location /read {\n        content_by_lua '\n            ngx.redirect(\"http://agentzh.org/foo\");\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://agentzh.org/foo\n--- response_body_like: 302 Found\n--- error_code: 302\n\n\n\n=== TEST 2: explicit 302\n--- config\n    location /read {\n        content_by_lua '\n            ngx.redirect(\"http://agentzh.org/foo\", ngx.HTTP_MOVED_TEMPORARILY);\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://agentzh.org/foo\n--- response_body_like: 302 Found\n--- error_code: 302\n\n\n\n=== TEST 3: explicit 301\n--- config\n    location /read {\n        content_by_lua '\n            ngx.redirect(\"http://agentzh.org/foo\", ngx.HTTP_MOVED_PERMANENTLY);\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://agentzh.org/foo\n--- response_body_like: 301 Moved Permanently\n--- error_code: 301\n\n\n\n=== TEST 4: bad rc\n--- config\n    location /read {\n        content_by_lua '\n            ngx.redirect(\"http://agentzh.org/foo\", 404);\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\n!Location\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nonly ngx.HTTP_MOVED_TEMPORARILY, ngx.HTTP_MOVED_PERMANENTLY, ngx.HTTP_PERMANENT_REDIRECT, ngx.HTTP_SEE_OTHER, and ngx.HTTP_TEMPORARY_REDIRECT are allowed\n\n\n\n=== TEST 5: no args\n--- config\n    location /read {\n        content_by_lua '\n            ngx.redirect()\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\n!Location\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 6: relative uri\n--- config\n    location /echo {\n        echo hello, world;\n    }\n    location /proxy {\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo;\n    }\n    location /read {\n        content_by_lua '\n            ngx.location.capture(\"/proxy\")\n            ngx.redirect(\"/echo\")\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /read\n--- raw_response_headers_like eval\nmy $headers;\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $headers = \"location: /echo\\r\\n\"\n} else {\n    $headers = \"Location: /echo\\r\\n\"\n}\n\n$headers;\n--- response_body_like: 302 Found\n--- error_code: 302\n\n\n\n=== TEST 7: default 302 (with uri args)\n--- config\n    location /read {\n        content_by_lua '\n            ngx.redirect(\"http://agentzh.org/foo?bar=3\");\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://agentzh.org/foo?bar=3\n--- response_body_like: 302 Found\n--- error_code: 302\n\n\n\n=== TEST 8: location.capture + ngx.redirect\n--- config\n    location /echo {\n        echo hello, world;\n    }\n    location /proxy {\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo;\n    }\n    location /read {\n        content_by_lua '\n            ngx.location.capture(\"/proxy\")\n            ngx.location.capture(\"/proxy\")\n            ngx.redirect(\"/echo\")\n            ngx.exit(403)\n        ';\n    }\n--- pipelined_requests eval\n[\"GET /read/1\", \"GET /read/2\"]\n--- error_code eval\n[302, 302]\n--- response_body eval\n[qr/302 Found/, qr/302 Found/]\n\n\n\n=== TEST 9: explicit 307\n--- config\n    location /read {\n        content_by_lua '\n            ngx.redirect(\"http://agentzh.org/foo\", ngx.HTTP_TEMPORARY_REDIRECT);\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://agentzh.org/foo\n--- response_body_like: 307 Temporary Redirect\n--- error_code: 307\n\n\n\n=== TEST 10: explicit 307 with args\n--- config\n    location /read {\n        content_by_lua '\n            ngx.redirect(\"http://agentzh.org/foo?a=b&c=d\", ngx.HTTP_TEMPORARY_REDIRECT);\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://agentzh.org/foo?a=b&c=d\n--- response_body_like: 307 Temporary Redirect\n--- error_code: 307\n\n\n\n=== TEST 11: explicit 307\n--- config\n    location /read {\n        content_by_lua '\n            ngx.redirect(\"http://agentzh.org/foo?a=b&c=d\", 307);\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://agentzh.org/foo?a=b&c=d\n--- response_body_like: 307 Temporary Redirect\n--- error_code: 307\n\n\n\n=== TEST 12: explicit 303\n--- config\n    location /read {\n        content_by_lua_block {\n            ngx.redirect(\"http://agentzh.org/foo\", ngx.HTTP_SEE_OTHER);\n            ngx.say(\"hi\")\n        }\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://agentzh.org/foo\n--- response_body_like: 303 See Other\n--- error_code: 303\n\n\n\n=== TEST 13: explicit 303 with args\n--- config\n    location /read {\n        content_by_lua_block {\n            ngx.redirect(\"http://agentzh.org/foo?a=b&c=d\", ngx.HTTP_SEE_OTHER);\n            ngx.say(\"hi\")\n        }\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://agentzh.org/foo?a=b&c=d\n--- response_body_like: 303 See Other\n--- error_code: 303\n\n\n\n=== TEST 14: explicit 303\n--- config\n    location /read {\n        content_by_lua_block {\n            ngx.redirect(\"http://agentzh.org/foo?a=b&c=d\", 303);\n            ngx.say(\"hi\")\n        }\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://agentzh.org/foo?a=b&c=d\n--- response_body_like: 303 See Other\n--- error_code: 303\n\n\n\n=== TEST 15: explicit 308 with args\n--- config\n    location /read {\n        content_by_lua '\n            ngx.redirect(\"http://agentzh.org/foo?a=b&c=d\", ngx.HTTP_PERMANENT_REDIRECT);\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /read\n--- response_body_like: 308 Permanent Redirect\n--- response_headers\nLocation: http://agentzh.org/foo?a=b&c=d\n--- error_code: 308\n\n\n\n=== TEST 16: explicit 308\n--- config\n    location /read {\n        content_by_lua '\n            ngx.redirect(\"http://agentzh.org/foo?a=b&c=d\", 308);\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /read\n--- response_body_like: 308 Permanent Redirect\n--- response_headers\nLocation: http://agentzh.org/foo?a=b&c=d\n--- error_code: 308\n\n\n\n=== TEST 17: explicit 308 with args\n--- config\n    location /read {\n        content_by_lua '\n            ngx.redirect(\"http://agentzh.org/foo?a=b&c=d\", 308);\n            ngx.say(\"hi\")\n        ';\n    }\n--- request\nGET /read\n--- response_body_like: 308 Permanent Redirect\n--- response_headers\nLocation: http://agentzh.org/foo?a=b&c=d\n--- error_code: 308\n\n\n\n=== TEST 18: unsafe uri (with '\\r')\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.redirect(\"http://agentzh.org/foo\\rfoo:bar\\nbar:foo\");\n            ngx.say(\"hi\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 500\n--- response_headers\nLocation:\nfoo:\nbar:\n--- error_log\nunsafe byte \"0x0d\" in redirect uri \"http://agentzh.org/foo\\x0Dfoo:bar\\x0Abar:foo\"\n\n\n\n=== TEST 19: unsafe uri (with '\\n')\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.redirect(\"http://agentzh.org/foo\\nfoo:bar\\rbar:foo\");\n            ngx.say(\"hi\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 500\n--- response_headers\nLocation:\nfoo:\nbar:\n--- error_log\nunsafe byte \"0x0a\" in redirect uri \"http://agentzh.org/foo\\x0Afoo:bar\\x0Dbar:foo\"\n\n\n\n=== TEST 20: unsafe uri (with prefix '\\n')\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.redirect(\"\\nfoo:http://agentzh.org/foo\");\n            ngx.say(\"hi\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 500\n--- response_headers\nLocation:\nfoo:\n--- error_log\nunsafe byte \"0x0a\" in redirect uri \"\\x0Afoo:http://agentzh.org/foo\"\n\n\n\n=== TEST 21: unsafe uri (with prefix '\\r')\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.redirect(\"\\rfoo:http://agentzh.org/foo\");\n            ngx.say(\"hi\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 500\n--- response_headers\nLocation:\nfoo:\n--- error_log\nunsafe byte \"0x0d\" in redirect uri \"\\x0Dfoo:http://agentzh.org/foo\"\n\n\n\n=== TEST 22: unsafe uri logging escapes '\"' and '\\' characters\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.redirect(\"\\rhttp\\\\://\\\"agentzh.org\\\"/foo\");\n            ngx.say(\"hi\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 500\n--- response_headers\nLocation:\nfoo:\n--- error_log\nunsafe byte \"0x0d\" in redirect uri \"\\x0Dhttp\\x5C://\\x22agentzh.org\\x22/foo\"\n"
  },
  {
    "path": "t/023-rewrite/client-abort.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = <<_EOC_;\n$t::StapThread::GCScript\n\nF(ngx_http_lua_check_broken_connection) {\n    println(\"lua check broken conn\")\n}\n\nF(ngx_http_lua_request_cleanup) {\n    println(\"lua req cleanup\")\n}\n_EOC_\n\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    plan(skip_all => \"HTTP3 does not support client abort\");\n} elsif (defined $ENV{TEST_NGINX_USE_HTTP2}) {\n    plan(skip_all => \"HTTP2 does not support client abort\");\n} else {\n    plan tests => repeat_each() * (blocks() * 3 - 1);\n}\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            ngx.sleep(1)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 0.1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 2: sleep + stop (log handler still gets called)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            ngx.sleep(1)\n        ';\n        log_by_lua '\n            ngx.log(ngx.NOTICE, \"here in log by lua\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\nhere in log by lua\n\n\n\n=== TEST 3: sleep + ignore\n--- config\n    location /t {\n        lua_check_client_abort off;\n        rewrite_by_lua '\n            ngx.sleep(1)\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nlua req cleanup\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: subrequest + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            ngx.location.capture(\"/sub\")\n            error(\"bad things happen\")\n        ';\n    }\n\n    location /sub {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 5: subrequest + ignore\n--- config\n    location /t {\n        lua_check_client_abort off;\n        rewrite_by_lua '\n            ngx.location.capture(\"/sub\")\n            error(\"bad things happen\")\n        ';\n    }\n\n    location /sub {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: fail\nlua req cleanup\ndelete thread 1\n\n--- wait: 1.1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- error_log\nbad things happen\n\n\n\n=== TEST 6: subrequest + stop (proxy, ignore client abort)\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            ngx.location.capture(\"/sub\")\n            error(\"bad things happen\")\n        ';\n    }\n\n    location = /sub {\n        proxy_ignore_client_abort on;\n        proxy_pass http://127.0.0.2:12345/;\n    }\n\n    location = /sleep {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            ngx.sleep(1)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 7: subrequest + stop (proxy, check client abort)\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            ngx.location.capture(\"/sub\")\n            error(\"bad things happen\")\n        ';\n    }\n\n    location = /sub {\n        proxy_ignore_client_abort off;\n        proxy_pass http://127.0.0.2:12345/;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 8: need body on + sleep + stop (log handler still gets called)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        lua_need_request_body on;\n        rewrite_by_lua '\n            ngx.sleep(1)\n        ';\n        log_by_lua '\n            ngx.log(ngx.NOTICE, \"here in log by lua\")\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\nhere in log by lua\n\n\n\n=== TEST 9: ngx.req.read_body + sleep + stop (log handler still gets called)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.sleep(1)\n        ';\n        log_by_lua '\n            ngx.log(ngx.NOTICE, \"here in log by lua\")\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\nhere in log by lua\n\n\n\n=== TEST 10: ngx.req.socket + receive() + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local sock = ngx.req.socket()\n            sock:receive()\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 11: ngx.req.socket + receive(N) + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local sock = ngx.req.socket()\n            sock:receive(5)\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 0.1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 12: ngx.req.socket + receive(n) + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local sock = ngx.req.socket()\n            sock:receive(2)\n            ngx.sleep(1)\n        ';\n        content_by_lua return;\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like\n^(?:lua check broken conn\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nlua req cleanup|lua check broken conn\nlua req cleanup\ndelete thread 1)$\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: ngx.req.socket + m * receive(n) + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local sock = ngx.req.socket()\n            sock:receive(2)\n            sock:receive(2)\n            sock:receive(1)\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 14: ngx.req.socket + receiveuntil + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local sock = ngx.req.socket()\n            local it = sock:receiveuntil(\"\\\\n\")\n            it()\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 15: ngx.req.socket + receiveuntil + it(n) + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local sock = ngx.req.socket()\n            local it = sock:receiveuntil(\"\\\\n\")\n            it(2)\n            it(3)\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 16: cosocket + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            ngx.req.discard_body()\n\n            local sock, err = ngx.socket.tcp()\n            if not sock then\n                ngx.log(ngx.ERR, \"failed to get socket: \", err)\n                return\n            end\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"blpop nonexist 2\\\\r\\\\n\")\n            if not bytes then\n                ngx.log(ngx.ERR, \"failed to send query: \", err)\n                return\n            end\n\n            -- ngx.log(ngx.ERR, \"about to receive\")\n\n            local res, err = sock:receive()\n            if not res then\n                ngx.log(ngx.ERR, \"failed to receive query: \", err)\n                return\n            end\n\n            ngx.log(ngx.ERR, \"res: \", res)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 17: ngx.req.socket + receive n < content-length + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local sock = ngx.req.socket()\n            local res, err = sock:receive(\"*a\")\n            if not res then\n                ngx.log(ngx.NOTICE, \"failed to receive: \", err)\n                return\n            end\n            error(\"bad\")\n        ';\n        content_by_lua return;\n    }\n--- raw_request eval\n\"POST /t HTTP/1.0\\r\nHost: localhost\\r\nConnection: close\\r\nContent-Length: 100\\r\n\\r\nhello\"\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nfailed to receive: client aborted\n\n\n\n=== TEST 18: ngx.req.socket + receive n == content-length + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local sock = ngx.req.socket()\n            local res, err = sock:receive(\"*a\")\n            if not res then\n                ngx.log(ngx.NOTICE, \"failed to receive: \", err)\n                return\n            end\n            ngx.sleep(1)\n            error(\"bad\")\n        ';\n        content_by_lua return;\n    }\n--- raw_request eval\n\"POST /t HTTP/1.0\\r\nHost: localhost\\r\nConnection: close\\r\nContent-Length: 5\\r\n\\r\nhello\"\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 19: ngx.req.socket + receive n == content-length + ignore\n--- config\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.req.socket()\n            local res, err = sock:receive(\"*a\")\n            if not res then\n                ngx.log(ngx.NOTICE, \"failed to receive: \", err)\n                return\n            end\n            ngx.say(\"done\")\n        ';\n        content_by_lua return;\n    }\n--- raw_request eval\n\"POST /t HTTP/1.0\\r\nHost: localhost\\r\nConnection: close\\r\nContent-Length: 5\\r\n\\r\nhello\"\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nlua req cleanup\n\n--- shutdown: 1\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 20: ngx.req.read_body + sleep + stop (log handler still gets called)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            ngx.req.read_body()\n        ';\n        content_by_lua return;\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nlua req cleanup\n\n--- shutdown: 1\n--- wait: 0.1\n--- ignore_response\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: exec to lua + ignore\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            ngx.exec(\"/t2\")\n        ';\n    }\n\n    location = /t2 {\n        lua_check_client_abort off;\n        content_by_lua '\n            ngx.sleep(1)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\nlua req cleanup\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nlua req cleanup\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 22: exec to proxy + ignore\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            ngx.exec(\"/t2\")\n        ';\n    }\n\n    location = /t2 {\n        proxy_ignore_client_abort on;\n        proxy_pass http://127.0.0.1:$server_port/sleep;\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 23: exec (named location) to proxy + ignore\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            ngx.exec(\"@t2\")\n        ';\n    }\n\n    location @t2 {\n        proxy_ignore_client_abort on;\n        proxy_pass http://127.0.0.1:$server_port/sleep;\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n"
  },
  {
    "path": "t/023-rewrite/exec.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 2 + 4);\n\n#no_diff();\n#no_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.exec(\"/hi\");\n            ngx.say(\"Hi\");\n        ';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body\nHello\n\n\n\n=== TEST 2: empty uri arg\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.exec(\"\");\n            ngx.say(\"Hi\");\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 3: no arg\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.exec();\n            ngx.say(\"Hi\");\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 4: too many args\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.exec(1, 2, 3, 4);\n            ngx.say(\"Hi\");\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 5: null uri\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.exec(nil)\n            ngx.say(\"Hi\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 6: user args\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.exec(\"/hi\", \"Yichun Zhang\")\n            ngx.say(\"Hi\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo Hello $query_string;\n    }\n--- request\nGET /read\n--- response_body\nHello Yichun Zhang\n\n\n\n=== TEST 7: args in uri\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.exec(\"/hi?agentzh\")\n            ngx.say(\"Hi\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo Hello $query_string;\n    }\n--- request\nGET /read\n--- response_body\nHello agentzh\n\n\n\n=== TEST 8: args in uri and user args\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.exec(\"/hi?a=Yichun\", \"b=Zhang\")\n            ngx.say(\"Hi\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo Hello $query_string;\n    }\n--- request\nGET /read\n--- response_body\nHello a=Yichun&b=Zhang\n\n\n\n=== TEST 9: args in uri and user args\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.exec(\"@hi?a=Yichun\", \"b=Zhang\")\n            ngx.say(\"Hi\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location @hi {\n        echo Hello $query_string;\n    }\n--- request\nGET /read\n--- response_body\nHello \n\n\n\n=== TEST 10: exec after location capture\n--- config\n    location /test {\n        rewrite_by_lua_file 'html/test.lua';\n        echo world;\n    }\n\n    location /a {\n        echo \"hello\";\n    }\n\n    location /b {\n        echo \"hello\";\n    }\n\n--- user_files\n>>> test.lua\nngx.location.capture('/a')\n\nngx.exec('/b')\n--- request\n    GET /test\n--- response_body\nhello\n\n\n\n=== TEST 11: exec after (named) location capture\n--- config\n    location /test {\n        rewrite_by_lua_file 'html/test.lua';\n    }\n\n    location /a {\n        echo \"hello\";\n    }\n\n    location @b {\n        echo \"hello\";\n    }\n\n--- user_files\n>>> test.lua\nngx.location.capture('/a')\n\nngx.exec('@b')\n--- request\n    GET /test\n--- response_body\nhello\n\n\n\n=== TEST 12: github issue #40: 2 Subrequest calls when using access_by_lua, ngx.exec and echo_location (rewrite)\n--- config\n    location = /hi {\n        echo hello;\n    }\n    location /sub {\n        proxy_pass http://127.0.0.1:$server_port/hi;\n    }\n    location /p{\n        #content_by_lua '\n        #local res = ngx.location.capture(\"/sub\")\n        #ngx.print(res.body)\n        #';\n        echo_location /sub;\n    }\n    location /lua {\n        rewrite_by_lua '\n            ngx.exec(\"/p\")\n        ';\n    }\n--- request\n    GET /lua\n--- response_body\nhello\n\n\n\n=== TEST 13: github issue #40: 2 Subrequest calls when using access_by_lua, ngx.exec and echo_location (rewrite + named location)\n--- config\n    location = /hi {\n        echo hello;\n    }\n    location /sub {\n        proxy_pass http://127.0.0.1:$server_port/hi;\n    }\n    location @p{\n        #content_by_lua '\n            #local res = ngx.location.capture(\"/sub\")\n            #ngx.print(res.body)\n        #';\n        echo_location /sub;\n    }\n    location /lua {\n        rewrite_by_lua '\n            ngx.exec(\"@p\")\n        ';\n    }\n--- request\n    GET /lua\n--- response_body\nhello\n\n\n\n=== TEST 14: github issue #40: 2 Subrequest calls when using access_by_lua, ngx.exec and echo_location (rewrite + post subrequest)\n--- config\n    location = /hi {\n        echo hello;\n    }\n    location /sub {\n        proxy_pass http://127.0.0.1:$server_port/hi;\n    }\n    location /p{\n        #content_by_lua '\n            #local res = ngx.location.capture(\"/sub\")\n            #ngx.print(res.body)\n        #';\n        echo_location /sub;\n    }\n    location /blah {\n        echo blah;\n    }\n    location /lua {\n        rewrite_by_lua '\n            ngx.location.capture(\"/blah\")\n            ngx.exec(\"/p\")\n        ';\n    }\n--- request\n    GET /lua\n--- response_body\nhello\n\n\n\n=== TEST 15: rewrite_by_lua + ngx.exec + subrequest capture\n--- config\n    location /main {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/test_loc\");\n            ngx.print(\"hello, \", res.body)\n        ';\n        content_by_lua return;\n    }\n    location /test_loc {\n        rewrite_by_lua '\n            ngx.exec(\"@proxy\")\n        ';\n    }\n    location @proxy {\n        #echo proxy;\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n    location /foo {\n        echo bah;\n    }\n--- request\n    GET /main\n--- response_body\nhello, bah\n\n\n\n=== TEST 16: rewrite_by_lua_file + ngx.exec + subrequest capture\n--- config\n    location /main {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/test_loc\");\n            ngx.print(\"hello, \", res.body)\n        ';\n        content_by_lua return;\n    }\n    location /test_loc {\n        rewrite_by_lua_file html/jump.lua;\n    }\n    location @proxy {\n        #echo proxy;\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n    location /foo {\n        echo bah;\n    }\n--- user_files\n>>> jump.lua\nngx.exec(\"@proxy\")\n--- request\n    GET /main\n--- response_body\nhello, bah\n\n\n\n=== TEST 17: pipelined requests\n--- config\n    location /t {\n        rewrite_by_lua_block {\n            ngx.exec(\"@foo\")\n        }\n    }\n\n    location @foo {\n        return 200;\n    }\n--- pipelined_requests eval\n[\"GET /t\", \"GET /t\"]\n--- error_code eval\n[200, 200]\n--- response_body eval\n[\"\", \"\"]\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/023-rewrite/exit.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#repeat_each(20000);\n\nrepeat_each(2);\n\n#master_on();\n#workers(1);\n#log_level('debug');\n#log_level('warn');\n#worker_connections(1024);\n\nplan tests => repeat_each() * (blocks() * 2 + 4);\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n\nour $LuaCpath = $ENV{LUA_CPATH} ||\n    '/usr/local/openresty-debug/lualib/?.so;/usr/local/openresty/lualib/?.so;;';\n\n#$ENV{LUA_PATH} = $ENV{HOME} . '/work/JSON4Lua-0.9.30/json/?.lua';\n\nno_long_string();\n#no_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: throw 403\n--- config\n    location /lua {\n        rewrite_by_lua \"ngx.exit(403);ngx.say('hi')\";\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- error_code: 403\n--- response_body_like: 403 Forbidden\n\n\n\n=== TEST 2: throw 404\n--- config\n    location /lua {\n        rewrite_by_lua \"ngx.exit(404);ngx.say('hi');\";\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- error_code: 404\n--- response_body_like: 404 Not Found\n\n\n\n=== TEST 3: throw 404 after sending the header and partial body\n--- config\n    location /lua {\n        rewrite_by_lua \"ngx.say('hi');ngx.exit(404);ngx.say(', you')\";\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- error_log\nattempt to set status 404 via ngx.exit after sending out the response status 200\n--- response_body\nhi\n\n\n\n=== TEST 4: working with ngx_auth_request (succeeded)\n--- config\n    location /auth {\n        rewrite_by_lua \"\n            if ngx.var.user == 'agentzh' then\n                ngx.eof();\n            else\n                ngx.exit(403)\n            end\";\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /api {\n        set $user $arg_user;\n        auth_request /auth;\n\n        echo \"Logged in\";\n    }\n--- request\nGET /api?user=agentzh\n--- error_code: 200\n--- response_body\nLogged in\n\n\n\n=== TEST 5: working with ngx_auth_request (failed)\n--- config\n    location /auth {\n        rewrite_by_lua \"\n            if ngx.var.user == 'agentzh' then\n                ngx.eof();\n            else\n                ngx.exit(403)\n            end\";\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /api {\n        set $user $arg_user;\n        auth_request /auth;\n\n        echo \"Logged in\";\n    }\n--- request\nGET /api?user=agentz\n--- error_code: 403\n--- response_body_like: 403 Forbidden\n\n\n\n=== TEST 6: working with ngx_auth_request (simplest form, w/o ngx_memc)\n--- no_http2\n--- http_config eval\n\"\n    lua_package_cpath '$::LuaCpath';\n    upstream backend {\n        drizzle_server 127.0.0.1:\\$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n\"\n--- config\n    location /memc {\n        internal;\n\n        set $memc_key $arg_key;\n        set $memc_exptime $arg_exptime;\n\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /conv-uid-mysql {\n        internal;\n\n        set $key \"conv-uid-$arg_uid\";\n\n        #srcache_fetch GET /memc key=$key;\n        #srcache_store PUT /memc key=$key;\n\n        default_type 'application/json';\n\n        drizzle_query \"select new_uid as uid from conv_uid where old_uid=$arg_uid\";\n        drizzle_pass backend;\n\n        rds_json on;\n    }\n\n    location /conv-uid {\n        internal;\n        rewrite_by_lua_file 'html/foo.lua';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /api {\n        set $uid $arg_uid;\n        auth_request /conv-uid;\n\n        echo \"Logged in $uid\";\n    }\n--- user_files\n>>> foo.lua\nlocal cjson = require('cjson');\nlocal old_uid = ngx.var.uid\nprint('about to run sr')\nlocal res = ngx.location.capture('/conv-uid-mysql?uid=' .. old_uid)\nprint('just have run sr' .. res.body)\nif (res.status ~= ngx.HTTP_OK) then\n    -- ngx.exit(res.status)\nend\nres = cjson.decode(res.body)\nif (not res or not res[1] or not res[1].uid or\n        not string.match(res[1].uid, '^%d+$')) then\n    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\nend\nngx.var.uid = res[1].uid;\n-- print('done')\n--- request\nGET /api?uid=32\n--- response_body\nLogged in 56\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 7: working with ngx_auth_request (simplest form)\n--- no_http2\n--- http_config eval\n\"\n    lua_package_cpath '$::LuaCpath';\n    upstream backend {\n        drizzle_server 127.0.0.1:\\$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n\"\n--- config\n    location /memc {\n        internal;\n\n        set $memc_key $arg_key;\n        set $memc_exptime $arg_exptime;\n\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /conv-uid-mysql {\n        internal;\n\n        set $key \"conv-uid-$arg_uid\";\n\n        #srcache_fetch GET /memc key=$key;\n        #srcache_store PUT /memc key=$key;\n\n        default_type 'application/json';\n\n        drizzle_query \"select new_uid as uid from conv_uid where old_uid=$arg_uid\";\n        drizzle_pass backend;\n\n        rds_json on;\n    }\n\n    location /conv-uid {\n        internal;\n        rewrite_by_lua_file 'html/foo.lua';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /api {\n        set $uid $arg_uid;\n        auth_request /conv-uid;\n\n        echo \"Logged in $uid\";\n    }\n--- user_files\n>>> foo.lua\nlocal cjson = require('cjson');\nlocal old_uid = ngx.var.uid\n-- print('about to run sr')\nlocal res = ngx.location.capture('/conv-uid-mysql?uid=' .. old_uid)\n-- print('just have run sr' .. res.body)\nif (res.status ~= ngx.HTTP_OK) then\n    ngx.exit(res.status)\nend\nres = cjson.decode(res.body)\nif (not res or not res[1] or not res[1].uid or\n        not string.match(res[1].uid, '^%d+$')) then\n    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\nend\nngx.var.uid = res[1].uid;\n-- print('done')\n--- request\nGET /api?uid=32\n--- response_body\nLogged in 56\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 8: working with ngx_auth_request\n--- no_http2\n--- http_config eval\n\"\n    lua_package_cpath '$::LuaCpath';\n    upstream backend {\n        drizzle_server 127.0.0.1:\\$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n\n    upstream memc_a {\n        server 127.0.0.1:\\$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    upstream memc_b {\n        server 127.0.0.1:\\$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    upstream_list memc_cluster memc_a memc_b;\n\"\n--- config\n    location /memc {\n        internal;\n\n        set $memc_key $arg_key;\n        set $memc_exptime $arg_exptime;\n\n        set_hashed_upstream $backend memc_cluster $arg_key;\n        memc_pass $backend;\n    }\n\n    location /conv-uid-mysql {\n        internal;\n\n        set $key \"conv-uid-$arg_uid\";\n\n        #srcache_fetch GET /memc key=$key;\n        #srcache_store PUT /memc key=$key;\n\n        default_type 'application/json';\n\n        drizzle_query \"select new_uid as uid from conv_uid where old_uid=$arg_uid\";\n        drizzle_pass backend;\n\n        rds_json on;\n    }\n\n    location /conv-uid {\n        internal;\n        rewrite_by_lua_file 'html/foo.lua';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /api {\n        set $uid $arg_uid;\n        auth_request /conv-uid;\n\n        echo \"Logged in $uid\";\n    }\n--- user_files\n>>> foo.lua\nlocal cjson = require('cjson');\nlocal old_uid = ngx.var.uid\n-- print('about to run sr')\nlocal res = ngx.location.capture('/conv-uid-mysql?uid=' .. old_uid)\n-- print('just have run sr' .. res.body)\nif (res.status ~= ngx.HTTP_OK) then\n    ngx.exit(res.status)\nend\nres = cjson.decode(res.body)\nif (not res or not res[1] or not res[1].uid or\n        not string.match(res[1].uid, '^%d+$')) then\n    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\nend\nngx.var.uid = res[1].uid;\n-- print('done')\n--- request\nGET /api?uid=32\n--- response_body\nLogged in 56\n--- no_error_log\n[error]\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 9: working with ngx_auth_request\n--- http_config\n    upstream backend {\n        drizzle_server 127.0.0.1:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n\n    upstream memc_a {\n        server 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n        keepalive 300;\n    }\n\n    #upstream_list memc_cluster memc_a memc_b;\n\n--- config\n    location /memc {\n        internal;\n\n        set $memc_key $arg_key;\n        set $memc_exptime $arg_exptime;\n\n        #set_hashed_upstream $backend memc_cluster $arg_key;\n        memc_pass memc_a;\n    }\n\n    location /conv-mysql {\n        internal;\n\n        set $key \"conv-uri-$query_string\";\n\n        #srcache_fetch GET /memc key=$key;\n        #srcache_store PUT /memc key=$key;\n\n        default_type 'application/json';\n\n        set_quote_sql_str $seo_uri $query_string;\n        drizzle_query \"select url from my_url_map where seo_url=$seo_uri\";\n        drizzle_pass backend;\n\n        rds_json on;\n    }\n\n    location /conv-uid {\n        internal;\n        rewrite_by_lua_file 'html/foo.lua';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n\n    location /baz {\n        set $my_uri $uri;\n        auth_request /conv-uid;\n\n        echo_exec /jump $my_uri;\n    }\n\n    location /jump {\n        internal;\n        rewrite ^ $query_string? redirect;\n    }\n--- user_files\n>>> foo.lua\nlocal cjson = require('cjson');\nlocal seo_uri = ngx.var.my_uri\n-- print('about to run sr')\nlocal res = ngx.location.capture('/conv-mysql?' .. seo_uri)\nif (res.status ~= ngx.HTTP_OK) then\n    ngx.exit(res.status)\nend\nres = cjson.decode(res.body)\nif (not res or not res[1] or not res[1].url) then\n    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\nend\nngx.var.my_uri = res[1].url;\n-- print('done')\n--- request\nGET /baz\n--- response_body_like: 302\n--- error_code: 302\n--- response_headers\nLocation: http://localhost:$ServerPort/foo/bar\n--- SKIP\n\n\n\n=== TEST 10: throw 0\n--- config\n    location /lua {\n        rewrite_by_lua \"ngx.say('Hi'); ngx.eof(); ngx.exit(0);ngx.say('world')\";\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- error_code: 200\n--- response_body\nHi\n\n\n\n=== TEST 11: throw ngx.OK does *not* skip other rewrite phase handlers\n--- config\n    location /lua {\n        rewrite_by_lua \"ngx.exit(ngx.OK)\";\n        set $foo hello;\n        echo $foo;\n    }\n--- request\nGET /lua\n--- response_body\nhello\n\n\n\n=== TEST 12: throw ngx.HTTP_OK *does* skip other rewrite phase handlers (by inlined code)\n--- config\n    location /lua {\n        rewrite_by_lua \"ngx.exit(ngx.HTTP_OK)\";\n        set $foo hello;\n        echo $foo;\n    }\n--- request\nGET /lua\n--- response_body\n\n\n\n=== TEST 13: throw ngx.HTTP_OK *does* skip other rewrite phase handlers (by inlined code + partial output)\n--- config\n    location /lua {\n        rewrite_by_lua \"ngx.say('hiya') ngx.exit(ngx.HTTP_OK)\";\n        set $foo hello;\n        echo $foo;\n    }\n--- request\nGET /lua\n--- response_body\nhiya\n\n\n\n=== TEST 14: throw ngx.HTTP_OK *does* skip other rewrite phase handlers (by file)\n--- config\n    location /lua {\n        rewrite_by_lua_file html/foo.lua;\n        set $foo hello;\n        echo $foo;\n    }\n--- user_files\n>>> foo.lua\nngx.exit(ngx.HTTP_OK)\n--- request\nGET /lua\n--- response_body\n\n\n\n=== TEST 15: throw ngx.HTTP_OK *does* skip other rewrite phase handlers (by file + partial output)\n--- config\n    location /lua {\n        rewrite_by_lua_file html/foo.lua;\n        set $foo hello;\n        echo $foo;\n    }\n--- user_files\n>>> foo.lua\nngx.say(\"morning\")\nngx.exit(ngx.HTTP_OK)\n--- request\nGET /lua\n--- response_body\nmorning\n\n\n\n=== TEST 16: error page with custom body\n--- config\n    error_page 410 @err;\n    location @err {\n        echo blah blah;\n    }\n    location /foo {\n        rewrite_by_lua '\n            ngx.status = ngx.HTTP_GONE\n            ngx.say(\"This is our own content\")\n            -- to cause quit the whole request rather than the current phase handler\n            ngx.exit(ngx.HTTP_OK)\n        ';\n        echo Hello;\n    }\n--- request\n    GET /foo\n--- response_body\nThis is our own content\n--- error_code: 410\n\n\n\n=== TEST 17: exit with 204 (HTTP 1.1)\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.exit(204)\n        ';\n\n        proxy_pass http://127.0.0.1:$server_port/blah;\n    }\n\n    location = /blah {\n        echo blah;\n    }\n--- request\nGET /t\n--- more_headers2\n--- stap2\nF(ngx_http_send_header) {\n    printf(\"send header\\n\")\n    print_ubacktrace()\n}\n--- response_body\n--- error_code: 204\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: exit with 204 (HTTP 1.0)\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.exit(204)\n        ';\n\n        proxy_pass http://127.0.0.1:$server_port/blah;\n    }\n\n    location = /blah {\n        echo blah;\n    }\n--- request\nGET /t HTTP/1.0\n--- more_headers2\n--- stap2\nF(ngx_http_send_header) {\n    printf(\"send header\\n\")\n    print_ubacktrace()\n}\n--- response_body\n--- error_code: 204\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/023-rewrite/mixed.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 2 + 3);\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: rewrite I/O with content I/O\n--- config\n    location /flush {\n        set $memc_cmd flush_all;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /memc {\n        set $memc_key $echo_request_uri;\n        set $memc_exptime 600;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            ngx.location.capture(\"/flush\");\n\n            local res = ngx.location.capture(\"/memc\");\n            print(\"rewrite GET: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n            print(\"rewrite PUT: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\");\n            print(\"rewrite cached: \" .. res.body);\n        ';\n\n        content_by_lua '\n            ngx.location.capture(\"/flush\");\n\n            local res = ngx.location.capture(\"/memc\");\n            ngx.say(\"content GET: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n            ngx.say(\"content PUT: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\");\n            ngx.say(\"content cached: \" .. res.body);\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ncontent GET: 404\ncontent PUT: 201\ncontent cached: hello\n--- grep_error_log eval: qr/rewrite .+?(?= while )/\n--- grep_error_log_out\nrewrite GET: 404\nrewrite PUT: 201\nrewrite cached: hello\n\n--- log_level: info\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 2: share data via nginx variables\n--- config\n    location /foo {\n        set $foo '';\n        rewrite_by_lua '\n            ngx.var.foo = 32\n        ';\n\n        content_by_lua '\n            ngx.say(tonumber(ngx.var.foo) * 2)\n        ';\n    }\n--- request\n    GET /foo\n--- response_body\n64\n\n\n\n=== TEST 3: share the request body (need request body explicitly off)\n--- config\n    location /echo_body {\n        lua_need_request_body off;\n        set $res '';\n        rewrite_by_lua '\n            ngx.var.res = ngx.var.request_body or \"nil\"\n        ';\n        content_by_lua '\n            ngx.say(ngx.var.res or \"nil\")\n            ngx.say(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body\nnil\nnil\n\n\n\n=== TEST 4: share the request body (need request body off by default)\n--- config\n    location /echo_body {\n        #lua_need_request_body off;\n        set $res '';\n        rewrite_by_lua '\n            ngx.var.res = ngx.var.request_body or \"nil\"\n        ';\n        content_by_lua '\n            ngx.say(ngx.var.res or \"nil\")\n            ngx.say(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body\nnil\nnil\n\n\n\n=== TEST 5: share the request body (need request body on)\n--- config\n    location /echo_body {\n        lua_need_request_body on;\n        set $res '';\n        rewrite_by_lua '\n            ngx.var.res = ngx.var.request_body or \"nil\"\n        ';\n        content_by_lua '\n            ngx.say(ngx.var.res or \"nil\")\n            ngx.say(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"hello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\n\"\n"
  },
  {
    "path": "t/023-rewrite/multi-capture.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(10);\n\nplan tests => blocks() * repeat_each() * 2;\n\n#$ENV{LUA_PATH} = $ENV{HOME} . '/work/JSON4Lua-0.9.30/json/?.lua';\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /foo {\n        rewrite_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n        ';\n        content_by_lua return;\n    }\n    location /a {\n        echo -n a;\n    }\n    location /b {\n        echo -n b;\n    }\n--- request\n    GET /foo\n--- response_body\nres1.status = 200\nres1.body = a\nres2.status = 200\nres2.body = b\n\n\n\n=== TEST 2: 4 concurrent requests\n--- config\n    location /foo {\n        rewrite_by_lua '\n            local res1, res2, res3, res4 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n                { \"/c\" },\n                { \"/d\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n\n            ngx.say(\"res3.status = \" .. res3.status)\n            ngx.say(\"res3.body = \" .. res3.body)\n\n            ngx.say(\"res4.status = \" .. res4.status)\n            ngx.say(\"res4.body = \" .. res4.body)\n        ';\n        content_by_lua return;\n    }\n    location ~ '^/([a-d])$' {\n        echo -n $1;\n    }\n--- request\n    GET /foo\n--- response_body\nres1.status = 200\nres1.body = a\nres2.status = 200\nres2.body = b\nres3.status = 200\nres3.body = c\nres4.status = 200\nres4.body = d\n\n\n\n=== TEST 3: capture multi in series\n--- config\n    location /foo {\n        rewrite_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n\n            res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            ngx.say(\"2 res1.status = \" .. res1.status)\n            ngx.say(\"2 res1.body = \" .. res1.body)\n            ngx.say(\"2 res2.status = \" .. res2.status)\n            ngx.say(\"2 res2.body = \" .. res2.body)\n\n        ';\n        content_by_lua return;\n    }\n    location /a {\n        echo -n a;\n    }\n    location /b {\n        echo -n b;\n    }\n--- request\n    GET /foo\n--- response_body\nres1.status = 200\nres1.body = a\nres2.status = 200\nres2.body = b\n2 res1.status = 200\n2 res1.body = a\n2 res2.status = 200\n2 res2.body = b\n\n\n\n=== TEST 4: capture multi in subrequest\n--- config\n    location /foo {\n        rewrite_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n\n            local n = ngx.var.arg_n\n\n            ngx.say(n .. \" res1.status = \" .. res1.status)\n            ngx.say(n .. \" res1.body = \" .. res1.body)\n            ngx.say(n .. \" res2.status = \" .. res2.status)\n            ngx.say(n .. \" res2.body = \" .. res2.body)\n        ';\n        content_by_lua return;\n    }\n\n    location /main {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo?n=1\")\n            ngx.say(\"top res.status = \" .. res.status)\n            ngx.say(\"top res.body = [\" .. res.body .. \"]\")\n        ';\n        content_by_lua return;\n    }\n\n    location /a {\n        echo -n a;\n    }\n\n    location /b {\n        echo -n b;\n    }\n--- request\n    GET /main\n--- response_body\ntop res.status = 200\ntop res.body = [1 res1.status = 200\n1 res1.body = a\n1 res2.status = 200\n1 res2.body = b\n]\n\n\n\n=== TEST 5: capture multi in parallel\n--- config\n    location ~ '^/(foo|bar)$' {\n        set $tag $1;\n        rewrite_by_lua '\n            local res1, res2\n            if ngx.var.tag == \"foo\" then\n                res1, res2 = ngx.location.capture_multi{\n                    { \"/a\" },\n                    { \"/b\" },\n                }\n            else\n                res1, res2 = ngx.location.capture_multi{\n                    { \"/c\" },\n                    { \"/d\" },\n                }\n            end\n\n            local n = ngx.var.arg_n\n\n            ngx.say(n .. \" res1.status = \" .. res1.status)\n            ngx.say(n .. \" res1.body = \" .. res1.body)\n            ngx.say(n .. \" res2.status = \" .. res2.status)\n            ngx.say(n .. \" res2.body = \" .. res2.body)\n        ';\n        content_by_lua return;\n    }\n\n    location /main {\n        rewrite_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/foo?n=1\" },\n                { \"/bar?n=2\" },\n            }\n\n            ngx.say(\"top res1.status = \" .. res1.status)\n            ngx.say(\"top res1.body = [\" .. res1.body .. \"]\")\n            ngx.say(\"top res2.status = \" .. res2.status)\n            ngx.say(\"top res2.body = [\" .. res2.body .. \"]\")\n        ';\n        content_by_lua return;\n    }\n\n    location ~ '^/([abcd])$' {\n        echo -n $1;\n    }\n--- request\n    GET /main\n--- response_body\ntop res1.status = 200\ntop res1.body = [1 res1.status = 200\n1 res1.body = a\n1 res2.status = 200\n1 res2.body = b\n]\ntop res2.status = 200\ntop res2.body = [2 res1.status = 200\n2 res1.body = c\n2 res2.status = 200\n2 res2.body = d\n]\n\n\n\n=== TEST 6: memc sanity\n--- config\n    location /foo {\n        rewrite_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n        ';\n        content_by_lua return;\n    }\n    location ~ '^/[ab]$' {\n        set $memc_key $uri;\n        set $memc_value hello;\n        set $memc_cmd set;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n--- request\n    GET /foo\n--- response_body eval\n\"res1.status = 201\nres1.body = STORED\\r\n\nres2.status = 201\nres2.body = STORED\\r\n\n\"\n\n\n\n=== TEST 7: memc muti + multi\n--- config\n    location /main {\n        rewrite_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/foo?n=1\" },\n                { \"/bar?n=2\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = [\" .. res1.body .. \"]\")\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = [\" .. res2.body .. \"]\")\n        ';\n        content_by_lua return;\n    }\n    location ~ '^/(foo|bar)$' {\n        set $tag $1;\n        rewrite_by_lua '\n            local res1, res2\n            if ngx.var.tag == \"foo\" then\n                res1, res2 = ngx.location.capture_multi{\n                    { \"/a\" },\n                    { \"/b\" },\n                }\n            else\n                res1, res2 = ngx.location.capture_multi{\n                    { \"/c\" },\n                    { \"/d\" },\n                }\n            end\n            print(\"args: \" .. ngx.var.args)\n            local n = ngx.var.arg_n\n            ngx.say(n .. \" res1.status = \" .. res1.status)\n            ngx.say(n .. \" res1.body = \" .. res1.body)\n            ngx.say(n .. \" res2.status = \" .. res2.status)\n            ngx.say(n .. \" res2.body = \" .. res2.body)\n        ';\n        content_by_lua return;\n    }\n    location ~ '^/[abcd]$' {\n        set $memc_key $uri;\n        set $memc_value hello;\n        set $memc_cmd set;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n--- request\n    GET /main\n--- response_body eval\n\"res1.status = 200\nres1.body = [1 res1.status = 201\n1 res1.body = STORED\\r\n\n1 res2.status = 201\n1 res2.body = STORED\\r\n\n]\nres2.status = 200\nres2.body = [2 res1.status = 201\n2 res1.body = STORED\\r\n\n2 res2.status = 201\n2 res2.body = STORED\\r\n\n]\n\"\n\n\n\n=== TEST 8: memc 4 concurrent requests\n--- config\n    location /foo {\n        rewrite_by_lua '\n            local res1, res2, res3, res4 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n                { \"/c\" },\n                { \"/d\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n\n            ngx.say(\"res3.status = \" .. res3.status)\n            ngx.say(\"res3.body = \" .. res3.body)\n\n            ngx.say(\"res4.status = \" .. res4.status)\n            ngx.say(\"res4.body = \" .. res4.body)\n        ';\n        content_by_lua return;\n    }\n    location ~ '^/[a-d]$' {\n        set $memc_key $uri;\n        set $memc_value hello;\n        set $memc_cmd set;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n--- request\n    GET /foo\n--- response_body eval\n\"res1.status = 201\nres1.body = STORED\\r\n\nres2.status = 201\nres2.body = STORED\\r\n\nres3.status = 201\nres3.body = STORED\\r\n\nres4.status = 201\nres4.body = STORED\\r\n\n\"\n"
  },
  {
    "path": "t/023-rewrite/on-abort.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = <<_EOC_;\n$t::StapThread::GCScript\n\nF(ngx_http_lua_check_broken_connection) {\n    println(\"lua check broken conn\")\n}\n\nF(ngx_http_lua_request_cleanup) {\n    println(\"lua req cleanup\")\n}\n_EOC_\n\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    plan(skip_all => \"HTTP3 does not support on_abort\");\n} elsif (defined $ENV{TEST_NGINX_USE_HTTP2}) {\n    plan(skip_all => \"HTTP2 does not support on_abort\");\n} else {\n    plan tests => repeat_each() * (blocks() * 4 + 15);\n}\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: ignore the client abort event in the user callback\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nterminate 3: ok\ndelete thread 3\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- wait: 0.7\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 2: abort in the user callback\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(444)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- wait: 0.1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\nmain handler done\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 3: ngx.exit(499) with pending subrequest\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(499)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n        ';\n        content_by_lua return;\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 4: ngx.exit(408) with pending subrequest\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(408)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n        ';\n        content_by_lua return;\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- wait: 0.1\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 5: ngx.exit(-1) with pending subrequest\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(-1)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n        ';\n        content_by_lua return;\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 6: ngx.exit(0) with pending subrequest\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(0)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n            ngx.log(ngx.ERR, \"main handler done\")\n        ';\n        content_by_lua return;\n    }\n\n    location = /sleep {\n        echo_sleep 0.7;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like eval\nqr/^create 2 in 1\nlua check broken conn\nterminate 2: fail\n(?:lua check broken conn\n)?terminate 1: ok\ndelete thread 2\ndelete thread 1\nterminate 3: ok\ndelete thread 3\nlua req cleanup\n$/\n--- timeout: 0.2\n--- abort\n--- wait: 0.6\n--- ignore_response\n--- error_log eval\n[\n'client prematurely closed connection',\n'on abort called',\nqr/lua user thread aborted: runtime error: rewrite_by_lua\\(nginx\\.conf:\\d+\\):4: attempt to abort with pending subrequests/,\n'main handler done',\n]\n\n\n\n=== TEST 7: accessing cosocket in callback\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n                if not ok then\n                    ngx.log(ngx.ERR, \"failed to connect to redis: \", err)\n                    ngx.exit(499)\n                end\n                local bytes, err = sock:send(\"flushall\\\\r\\\\n\")\n                if not bytes then\n                    ngx.log(ngx.ERR, \"failed to send query: \", err)\n                    ngx.exit(499)\n                end\n\n                local res, err = sock:receive()\n                if not res then\n                    ngx.log(ngx.ERR, \"failed to receive: \", err)\n                    ngx.exit(499)\n                end\n                ngx.log(ngx.NOTICE, \"callback done: \", res)\n                ngx.exit(499)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- wait: 0.5\n--- ignore_response\n--- no_error_log\n[error]\nmain handler done\n--- error_log\nclient prematurely closed connection\non abort called\ncallback done: +OK\n\n\n\n=== TEST 8: ignore the client abort event in the user callback (no check)\n--- config\n    location /t {\n        lua_check_client_abort off;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                ngx.say(\"cannot set on_abort: \", err)\n                return\n            end\n\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- response_body\ncannot set on_abort: lua_check_client_abort is off\n--- no_error_log\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 9: register on_abort callback but no client abortion\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nlua req cleanup\ndelete thread 2\n\n--- response_body\ndone\n--- no_error_log\n[error]\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 10: ignore the client abort event in the user callback (uthread)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.7)\n                ngx.log(ngx.NOTICE, \"main handler done\")\n            end)\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nlua check broken conn\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\nterminate 4: ok\ndelete thread 4\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- wait: 0.7\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 11: abort in the user callback (uthread)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(444)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.7)\n                ngx.log(ngx.NOTICE, \"main handler done\")\n            end)\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 3\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\nmain handler done\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 12: register on_abort callback but no client abortion (uthread)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.1)\n                ngx.say(\"done\")\n            end)\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 3: ok\ndelete thread 3\nlua req cleanup\ndelete thread 2\n\n--- wait: 0.5\n--- response_body\ndone\n--- no_error_log\n[error]\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 13: register on_abort callback multiple times\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                ngx.say(\"1: cannot set on_abort: \" .. err)\n                return\n            end\n\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                ngx.say(\"2: cannot set on_abort: \" .. err)\n                return\n            end\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.1)\n                ngx.say(\"done\")\n            end)\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nlua req cleanup\ndelete thread 2\n\n--- response_body\n2: cannot set on_abort: duplicate call\n\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/023-rewrite/redirect.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => blocks() * repeat_each() * 3;\n\n#no_diff();\n#no_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: default 302\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.redirect(\"http://www.taobao.com/foo\");\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://www.taobao.com/foo\n--- response_body_like: 302 Found\n--- error_code: 302\n\n\n\n=== TEST 2: explicit 302\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.redirect(\"http://www.taobao.com/foo\", ngx.HTTP_MOVED_TEMPORARILY);\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://www.taobao.com/foo\n--- response_body_like: 302 Found\n--- error_code: 302\n\n\n\n=== TEST 3: explicit 301\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.redirect(\"http://www.taobao.com/foo\", ngx.HTTP_MOVED_PERMANENTLY);\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://www.taobao.com/foo\n--- response_body_like: 301 Moved Permanently\n--- error_code: 301\n\n\n\n=== TEST 4: bad rc\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.redirect(\"http://www.taobao.com/foo\", 404);\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n    }\n--- request\nGET /read\n--- response_headers\n!Location\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 5: no args\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.redirect()\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n    }\n--- request\nGET /read\n--- response_headers\n!Location\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 6: relative uri\n--- config\n    location /read {\n        rewrite_by_lua '\n            ngx.redirect(\"/foo\")\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n    }\n--- request\nGET /read\n--- raw_response_headers_like eval\nmy $headers;\n\nif (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    $headers = \"location: /foo\\r\\n\"\n} else {\n    $headers = \"Location: /foo\\r\\n\"\n}\n\n$headers;\n--- response_body_like: 302 Found\n--- error_code: 302\n\n\n\n=== TEST 7: internal redirects that do not clear module ctx\n--- http_config\n    rewrite_by_lua_no_postpone on;\n--- config\n\trewrite_by_lua_block {\n        -- this is empty by intention\n\t}\n\n\tlocation /url1 {\n\t\trewrite ^ /url2;\n\t}\n--- request\nGET /url1\n--- response_body_like: 404 Not Found\n--- error_log eval\nqr{\\[error\\] .*?/html/url2\".*?No such file or directory}\n--- error_code: 404\n\n\n\n=== TEST 8: internal redirects that do not clear module ctx (yield in rewrite handler)\n--- http_config\n    rewrite_by_lua_no_postpone on;\n--- config\n\trewrite_by_lua_block {\n        ngx.sleep(0.001)\n\t}\n\n\tlocation /url1 {\n\t\trewrite ^ /url2;\n\t}\n--- request\nGET /url1\n--- response_body_like: 404 Not Found\n--- error_log eval\nqr{\\[error\\] .*?/html/url2\".*?No such file or directory}\n--- error_code: 404\n"
  },
  {
    "path": "t/023-rewrite/req-body.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4 + 5);\n\n#no_diff();\n#no_long_string();\n#master_on();\n#workers(2);\nrun_tests();\n\n__DATA__\n\n=== TEST 1: read buffered body\n--- config\n    location = /test {\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_body)\n        ';\n        content_by_lua return;\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nhello, world\n\n\n\n=== TEST 2: read buffered body (timed out)\n--- config\n    client_body_timeout 1ms;\n    location = /test {\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_body)\n        ';\n        content_by_lua return;\n    }\n--- raw_request eval\n\"POST /test HTTP/1.1\\r\nHost: localhost\\r\nContent-Length: 100\\r\nConnection: close\\r\n\\r\nhello, world\"\n--- response_body:\n--- error_code_like: ^(?:500)?$\n\n\n\n=== TEST 3: read buffered body and then subrequest\n--- config\n    location /foo {\n        echo -n foo;\n    }\n    location = /test {\n        rewrite_by_lua '\n            ngx.req.read_body()\n            local res = ngx.location.capture(\"/foo\");\n            ngx.say(ngx.var.request_body)\n            ngx.say(\"sub: \", res.body)\n        ';\n        content_by_lua return;\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nhello, world\nsub: foo\n\n\n\n=== TEST 4: first subrequest and then read buffered body\n--- config\n    location /foo {\n        echo -n foo;\n    }\n    location = /test {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\");\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_body)\n            ngx.say(\"sub: \", res.body)\n        ';\n        content_by_lua return;\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nhello, world\nsub: foo\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 5: failed to write 100 continue\n--- config\n    location = /test {\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_body)\n            ngx.exit(200)\n        ';\n    }\n--- request\nPOST /test\nhello, world\n--- more_headers\nExpect: 100-Continue\n--- ignore_response\n--- no_error_log\n[alert]\n[error]\nhttp finalize request: 500, \"/test?\" a:1, c:0\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 6: not discard body (exit 200)\n--- config\n    location = /foo {\n        rewrite_by_lua '\n            -- ngx.req.discard_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n            ngx.exit(200)\n        ';\n    }\n    location = /bar {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /foo\nhello, world\",\n\"POST /bar\nhiya, world\"]\n--- response_body eval\n[\"body: nil\\n\",\n\"body: hiya, world\\n\",\n]\n--- error_code eval\n[200, 200]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 7: not discard body (exit 201)\n--- config\n    location = /foo {\n        rewrite_by_lua '\n            -- ngx.req.discard_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n            ngx.exit(201)\n        ';\n    }\n    location = /bar {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /foo\nhello, world\",\n\"POST /bar\nhiya, world\"]\n--- response_body eval\n[\"body: nil\\n\",\n\"body: hiya, world\\n\",\n]\n--- error_code eval\n[200, 200]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 8: not discard body (exit 302)\n--- config\n    location = /foo {\n        rewrite_by_lua '\n            -- ngx.req.discard_body()\n            -- ngx.say(\"body: \", ngx.var.request_body)\n            ngx.redirect(\"/blah\")\n        ';\n    }\n    location = /bar {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /foo\nhello, world\",\n\"POST /bar\nhiya, world\"]\n--- response_body eval\n[qr/302 Found/,\n\"body: hiya, world\\n\",\n]\n--- error_code eval\n[302, 200]\n--- no_error_log\n[error]\n[alert]\n"
  },
  {
    "path": "t/023-rewrite/req-socket.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_USE_HTTP3}) {\n        $SkipReason = \"http3 does not support ngx.req.socket\";\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 3);\n\nour $HtmlDir = html_dir;\n\n#$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\nno_long_string();\n#no_diff();\n#log_level 'warn';\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /t {\n        rewrite_by_lua '\n            local sock, err = ngx.req.socket()\n            if sock then\n                ngx.say(\"got the request socket\")\n            else\n                ngx.say(\"failed to get the request socket: \", err)\n            end\n\n            for i = 1, 3 do\n                local data, err, part = sock:receive(5)\n                if data then\n                    ngx.say(\"received: \", data)\n                else\n                    ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n                end\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request\nPOST /t\nhello world\n--- response_body\ngot the request socket\nreceived: hello\nreceived:  worl\nfailed to receive: closed [d]\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: multipart rfc sample (just partial streaming)\n--- config\n    location /t {\n        rewrite_by_lua '\n            local sock, err = ngx.req.socket()\n            if sock then\n                ngx.say(\"got the request socket\")\n            else\n                ngx.say(\"failed to get the request socket: \", err)\n            end\n\n            local boundary\n            local header = ngx.var.http_content_type\n            local m = ngx.re.match(header, [[; +boundary=(?:\"(.*?)\"|(\\\\w+))]], \"jo\")\n            if m then\n                boundary = m[1] or m[2]\n\n            else\n                ngx.say(\"invalid content-type header\")\n                return\n            end\n\n            local read_to_boundary = sock:receiveuntil(\"\\\\r\\\\n--\" .. boundary)\n            local read_line = sock:receiveuntil(\"\\\\r\\\\n\")\n\n            local data, err, part = read_to_boundary()\n            if data then\n                ngx.say(\"preamble: [\" .. data .. \"]\")\n            else\n                ngx.say(\"failed to read the first boundary: \", err)\n                return\n            end\n\n            local i = 1\n            while true do\n                local line, err = read_line()\n\n                if not line then\n                    ngx.say(\"failed to read post-boundary line: \", err)\n                    return\n                end\n\n                m = ngx.re.match(line, \"--$\", \"jo\")\n                if m then\n                    ngx.say(\"found the end of the stream\")\n                    return\n                end\n\n                while true do\n                    local line, err = read_line()\n                    if not line then\n                        ngx.say(\"failed to read part \", i, \" header: \", err)\n                        return\n                    end\n\n                    if line == \"\" then\n                        -- the header part completes\n                        break\n                    end\n\n                    ngx.say(\"part \", i, \" header: [\", line, \"]\")\n                end\n\n                local data, err, part = read_to_boundary()\n                if data then\n                    ngx.say(\"part \", i, \" body: [\" .. data .. \"]\")\n                else\n                    ngx.say(\"failed to read part \", i + 1, \" boundary: \", err)\n                    return\n                end\n\n                i = i + 1\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request eval\n\"POST /t\nThis is the preamble.  It is to be ignored, though it\nis a handy place for mail composers to include an\nexplanatory note to non-MIME compliant readers.\\r\n--simple boundary\\r\n\\r\nThis is implicitly typed plain ASCII text.\nIt does NOT end with a linebreak.\\r\n--simple boundary\\r\nContent-type: text/plain; charset=us-ascii\\r\n\\r\nThis is explicitly typed plain ASCII text.\nIt DOES end with a linebreak.\n\\r\n--simple boundary--\\r\nThis is the epilogue.  It is also to be ignored.\n\"\n--- more_headers\nContent-Type: multipart/mixed; boundary=\"simple boundary\"\n--- response_body\ngot the request socket\npreamble: [This is the preamble.  It is to be ignored, though it\nis a handy place for mail composers to include an\nexplanatory note to non-MIME compliant readers.]\npart 1 body: [This is implicitly typed plain ASCII text.\nIt does NOT end with a linebreak.]\npart 2 header: [Content-type: text/plain; charset=us-ascii]\npart 2 body: [This is explicitly typed plain ASCII text.\nIt DOES end with a linebreak.\n]\nfound the end of the stream\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: multipart rfc sample (completely streaming)\n--- config\n    location /t {\n        rewrite_by_lua '\n            local sock, err = ngx.req.socket()\n            if sock then\n                ngx.say(\"got the request socket\")\n            else\n                ngx.say(\"failed to get the request socket: \", err)\n            end\n\n            local boundary\n            local header = ngx.var.http_content_type\n            local m = ngx.re.match(header, [[; +boundary=(?:\"(.*?)\"|(\\\\w+))]], \"jo\")\n            if m then\n                boundary = m[1] or m[2]\n\n            else\n                ngx.say(\"invalid content-type header\")\n                return\n            end\n\n            local read_to_boundary = sock:receiveuntil(\"\\\\r\\\\n--\" .. boundary)\n            local read_line = sock:receiveuntil(\"\\\\r\\\\n\")\n\n            local preamble = \"\"\n            while true do\n                local data, err, part = read_to_boundary(1)\n                if data then\n                    preamble = preamble .. data\n\n                elseif not err then\n                    break\n\n                else\n                    ngx.say(\"failed to read the first boundary: \", err)\n                    return\n                end\n            end\n\n            ngx.say(\"preamble: [\" .. preamble .. \"]\")\n\n            local i = 1\n            while true do\n                local line, err = read_line(50)\n\n                if not line and err then\n                    ngx.say(\"1: failed to read post-boundary line: \", err)\n                    return\n                end\n\n                if line then\n                    local dummy\n                    dummy, err = read_line(1)\n                    if err then\n                        ngx.say(\"2: failed to read post-boundary line: \", err)\n                        return\n                    end\n\n                    if dummy then\n                        ngx.say(\"bad post-boundary line: \", dummy)\n                        return\n                    end\n\n                    m = ngx.re.match(line, \"--$\", \"jo\")\n                    if m then\n                        ngx.say(\"found the end of the stream\")\n                        return\n                    end\n                end\n\n                while true do\n                    local line, err = read_line(50)\n                    if not line and err then\n                        ngx.say(\"failed to read part \", i, \" header: \", err)\n                        return\n                    end\n\n                    if line then\n                        local line, err = read_line(1)\n                        if line or err then\n                            ngx.say(\"error\")\n                            return\n                        end\n                    end\n\n                    if line == \"\" then\n                        -- the header part completes\n                        break\n                    end\n\n                    ngx.say(\"part \", i, \" header: [\", line, \"]\")\n                end\n\n                local body = \"\"\n\n                while true do\n                    local data, err, part = read_to_boundary(1)\n                    if data then\n                        body = body .. data\n\n                    elseif err then\n                        ngx.say(\"failed to read part \", i + 1, \" boundary: \", err)\n                        return\n\n                    else\n                        break\n                    end\n                end\n\n                ngx.say(\"part \", i, \" body: [\" .. body .. \"]\")\n\n                i = i + 1\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request eval\n\"POST /t\nThis is the preamble.  It is to be ignored, though it\nis a handy place for mail composers to include an\nexplanatory note to non-MIME compliant readers.\\r\n--simple boundary\\r\n\\r\nThis is implicitly typed plain ASCII text.\nIt does NOT end with a linebreak.\\r\n--simple boundary\\r\nContent-type: text/plain; charset=us-ascii\\r\n\\r\nThis is explicitly typed plain ASCII text.\nIt DOES end with a linebreak.\n\\r\n--simple boundary--\\r\nThis is the epilogue.  It is also to be ignored.\n\"\n--- more_headers\nContent-Type: multipart/mixed; boundary=\"simple boundary\"\n--- response_body\ngot the request socket\npreamble: [This is the preamble.  It is to be ignored, though it\nis a handy place for mail composers to include an\nexplanatory note to non-MIME compliant readers.]\npart 1 body: [This is implicitly typed plain ASCII text.\nIt does NOT end with a linebreak.]\npart 2 header: [Content-type: text/plain; charset=us-ascii]\npart 2 body: [This is explicitly typed plain ASCII text.\nIt DOES end with a linebreak.\n]\nfound the end of the stream\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: attempt to use the req socket across request boundary\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        rewrite_by_lua '\n            local test = require \"test\"\n            test.go()\n            ngx.say(\"done\")\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock, err\n\nfunction go()\n    if not sock then\n        sock, err = ngx.req.socket()\n        if sock then\n            ngx.say(\"got the request socket\")\n        else\n            ngx.say(\"failed to get the request socket: \", err)\n        end\n    else\n        for i = 1, 3 do\n            local data, err, part = sock:receive(5)\n            if data then\n                ngx.say(\"received: \", data)\n            else\n                ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n            end\n        end\n    end\nend\n--- request\nPOST /t\nhello world\n--- response_body_like\n(?:got the request socket\n|failed to receive: closed [d]\n)?done\n--- no_error_log\n[alert]\n\n\n\n=== TEST 5: receive until on request_body - receiveuntil(1) on the last byte of the body\nSee https://groups.google.com/group/openresty/browse_thread/thread/43cf01da3c681aba for details\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        rewrite_by_lua '\n            local test = require \"test\"\n            test.go()\n            ngx.say(\"done\")\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go()\n   local sock, err = ngx.req.socket()\n   if sock then\n      ngx.say(\"got the request socket\")\n   else\n      ngx.say(\"failed to get the request socket: \", err)\n      return\n   end\n\n   local data, err, part = sock:receive(56)\n   if data then\n      ngx.say(\"received: \", data)\n   else\n      ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n   end\n\n   local discard_line = sock:receiveuntil('\\r\\n')\n\n   local data, err, part = discard_line(8192)\n   if data then\n      ngx.say(\"received len: \", #data)\n   else\n      ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n   end\n\n   local data, err, part = discard_line(1)\n   if data then\n      ngx.say(\"received: \", data)\n   else\n      ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n   end\nend\n--- request\nPOST /t\n-----------------------------820127721219505131303151179################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################$\n--- response_body\ngot the request socket\nreceived: -----------------------------820127721219505131303151179\nreceived len: 8192\nreceived: $\ndone\n--- no_error_log\n[error]\n--- timeout: 10\n\n\n\n=== TEST 6: pipelined POST requests\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        rewrite_by_lua '\n            local test = require \"test\"\n            test.go()\n            ngx.say(\"done\")\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go()\n   local sock, err = ngx.req.socket()\n   if sock then\n      ngx.say(\"got the request socket\")\n   else\n      ngx.say(\"failed to get the request socket: \", err)\n      return\n   end\n\n   while true do\n       local data, err, part = sock:receive(4)\n       if data then\n          ngx.say(\"received: \", data)\n       else\n          ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n          return\n       end\n   end\nend\n--- pipelined_requests eval\n[\"POST /t\nhello, world\",\n\"POST /t\nhiya, world\"]\n--- response_body eval\n[\"got the request socket\nreceived: hell\nreceived: o, w\nreceived: orld\nfailed to receive: closed []\ndone\n\",\n\"got the request socket\nreceived: hiya\nreceived: , wo\nfailed to receive: closed [rld]\ndone\n\"]\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: Expect & 100 Continue\n--- config\n    location /t {\n        rewrite_by_lua '\n            local sock, err = ngx.req.socket()\n            if sock then\n                ngx.say(\"got the request socket\")\n            else\n                ngx.say(\"failed to get the request socket: \", err)\n                return\n            end\n\n            for i = 1, 3 do\n                local data, err, part = sock:receive(5)\n                if data then\n                    ngx.say(\"received: \", data)\n                else\n                    ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n                end\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request\nPOST /t\nhello world\n--- more_headers\nExpect: 100-Continue\n--- error_code: 100\n--- response_body_like chomp\n\\breceived: hello\\b.*?\\breceived:  worl\\b\n--- no_error_log\n[error]\n--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n"
  },
  {
    "path": "t/023-rewrite/request_body.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('debug'); # to ensure any log-level can be outputted\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 2);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: test reading request body\n--- config\n    location /echo_body {\n        lua_need_request_body on;\n        rewrite_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"hello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n\n\n\n=== TEST 2: test not reading request body\n--- config\n    location /echo_body {\n        lua_need_request_body off;\n        rewrite_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"nil\"\n\n\n\n=== TEST 3: test default setting (not reading request body)\n--- config\n    location /echo_body {\n        rewrite_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"nil\"\n\n\n\n=== TEST 4: test main conf\n--- http_config\n    lua_need_request_body on;\n--- config\n    location /echo_body {\n        rewrite_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"hello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n\n\n\n=== TEST 5: test server conf\n--- config\n    lua_need_request_body on;\n\n    location /echo_body {\n        rewrite_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"hello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n\n\n\n=== TEST 6: test override main conf\n--- http_config\n    lua_need_request_body on;\n--- config\n    location /echo_body {\n        lua_need_request_body off;\n        rewrite_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"nil\"\n\n\n\n=== TEST 7: test override server conf\n--- config\n    lua_need_request_body on;\n\n    location /echo_body {\n        lua_need_request_body off;\n        rewrite_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"nil\"\n\n\n\n=== TEST 8: Expect: 100-Continue\n--- config\n    location /echo_body {\n        lua_need_request_body on;\n        rewrite_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n            ngx.exit(200)\n        ';\n    }\n--- request\nPOST /echo_body\nhello world\n--- more_headers\nExpect: 100-Continue\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\nhttp finalize request: 500, \"/echo_body?\" a:1, c:2\nhttp finalize request: 500, \"/echo_body?\" a:1, c:0\n--- log_level: debug\n"
  },
  {
    "path": "t/023-rewrite/sanity.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#no_nginx_manager();\n#log_level('warn');\n#master_on();\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 12);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: basic print\n--- config\n    location /lua {\n        # NOTE: the newline escape sequence must be double-escaped, as nginx config\n        # parser will unescape first!\n        rewrite_by_lua 'ngx.print(\"Hello, Lua!\\\\n\")';\n        content_by_lua return;\n        #content_by_lua 'ngx.say(\"Hi\")';\n    }\n--- request\nGET /lua\n--- response_body\nHello, Lua!\n\n\n\n=== TEST 2: basic say\n--- config\n    location /say {\n        # NOTE: the newline escape sequence must be double-escaped, as nginx config\n        # parser will unescape first!\n        rewrite_by_lua '\n            ngx.say(\"Hello, Lua!\")\n            ngx.say(\"Yay! \", 123)';\n\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /say\n--- response_body\nHello, Lua!\nYay! 123\n\n\n\n=== TEST 3: no ngx.echo\n--- config\n    location /lua {\n        rewrite_by_lua 'ngx.echo(\"Hello, Lua!\\\\n\")';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 4: variable\n--- config\n    location /lua {\n        # NOTE: the newline escape sequence must be double-escaped, as nginx config\n        # parser will unescape first!\n        rewrite_by_lua 'local v = ngx.var[\"request_uri\"] ngx.print(\"request_uri: \", v, \"\\\\n\")';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua?a=1&b=2\n--- response_body\nrequest_uri: /lua?a=1&b=2\n\n\n\n=== TEST 5: variable (file)\n--- config\n    location /lua {\n        rewrite_by_lua_file html/test.lua;\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- user_files\n>>> test.lua\nlocal v = ngx.var[\"request_uri\"]\nngx.print(\"request_uri: \", v, \"\\n\")\n--- request\nGET /lua?a=1&b=2\n--- response_body\nrequest_uri: /lua?a=1&b=2\n\n\n\n=== TEST 6: calc expression\n--- config\n    location /lua {\n        rewrite_by_lua_file html/calc.lua;\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- user_files\n>>> calc.lua\nlocal function uri_unescape(uri)\n    local function convert(hex)\n        return string.char(tonumber(\"0x\"..hex))\n    end\n    local s = string.gsub(uri, \"%%([0-9a-fA-F][0-9a-fA-F])\", convert)\n    return s\nend\n\nlocal function eval_exp(str)\n    return loadstring(\"return \"..str)()\nend\n\nlocal exp_str = ngx.var[\"arg_exp\"]\n-- print(\"exp: '\", exp_str, \"'\\n\")\nlocal status, res\nstatus, res = pcall(uri_unescape, exp_str)\nif not status then\n    ngx.print(\"error: \", res, \"\\n\")\n    return\nend\nstatus, res = pcall(eval_exp, res)\nif status then\n    ngx.print(\"result: \", res, \"\\n\")\nelse\n    ngx.print(\"error: \", res, \"\\n\")\nend\n--- request\nGET /lua?exp=1%2B2*math.sin(3)%2Fmath.exp(4)-math.sqrt(2)\n--- response_body\nresult: -0.4090441561579\n\n\n\n=== TEST 7: read $arg_xxx\n--- config\n    location = /lua {\n        rewrite_by_lua 'local who = ngx.var.arg_who\n            ngx.print(\"Hello, \", who, \"!\")';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua?who=agentzh\n--- response_body chomp\nHello, agentzh!\n\n\n\n=== TEST 8: capture location\n--- config\n    location /other {\n        echo \"hello, world\";\n    }\n\n    location /lua {\n        rewrite_by_lua '\nlocal res = ngx.location.capture(\"/other\")\nngx.print(\"status=\", res.status, \" \")\nngx.print(\"body=\", res.body)\n';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nstatus=200 body=hello, world\n\n\n\n=== TEST 9: capture non-existed location\n--- config\n    location /lua {\n        rewrite_by_lua 'local res = ngx.location.capture(\"/other\"); ngx.print(\"status=\", res.status)';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body: status=404\n\n\n\n=== TEST 10: invalid capture location (not as expected...)\n--- config\n    location /lua {\n        rewrite_by_lua 'local res = ngx.location.capture(\"*(#*\"); ngx.say(\"res=\", res.status)';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nres=404\n\n\n\n=== TEST 11: nil is \"nil\"\n--- config\n    location /lua {\n        rewrite_by_lua 'ngx.say(nil)';\n        content_by_lua return;\n    }\n--- request\nGET /lua\n--- response_body\nnil\n\n\n\n=== TEST 12: write boolean\n--- config\n    location /lua {\n        rewrite_by_lua 'ngx.say(true, \" \", false)';\n        content_by_lua return;\n    }\n--- request\nGET /lua\n--- response_body\ntrue false\n\n\n\n=== TEST 13: bad argument type to ngx.location.capture\n--- config\n    location /lua {\n        rewrite_by_lua 'ngx.location.capture(nil)';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 14: capture location (default 0);\n--- config\n location /recur {\n       rewrite_by_lua '\n           local num = tonumber(ngx.var.arg_num) or 0;\n           ngx.print(\"num is: \", num, \"\\\\n\");\n\n           if (num > 0) then\n               res = ngx.location.capture(\"/recur?num=\"..tostring(num - 1));\n               ngx.print(\"status=\", res.status, \" \");\n               ngx.print(\"body=\", res.body, \"\\\\n\");\n           else\n               ngx.print(\"end\\\\n\");\n           end\n           ';\n\n           content_by_lua 'ngx.exit(ngx.OK)';\n   }\n--- request\nGET /recur\n--- response_body\nnum is: 0\nend\n\n\n\n=== TEST 15: capture location\n--- config\n location /recur {\n       rewrite_by_lua '\n           local num = tonumber(ngx.var.arg_num) or 0;\n           ngx.print(\"num is: \", num, \"\\\\n\");\n\n           if (num > 0) then\n               local res = ngx.location.capture(\"/recur?num=\"..tostring(num - 1));\n               ngx.print(\"status=\", res.status, \" \");\n               ngx.print(\"body=\", res.body);\n           else\n               ngx.print(\"end\\\\n\");\n           end\n           ';\n\n           content_by_lua 'ngx.exit(ngx.OK)';\n   }\n--- request\nGET /recur?num=3\n--- response_body\nnum is: 3\nstatus=200 body=num is: 2\nstatus=200 body=num is: 1\nstatus=200 body=num is: 0\nend\n\n\n\n=== TEST 16: setting nginx variables from within Lua\n--- config\n location /set {\n       set $a \"\";\n       rewrite_by_lua 'ngx.var.a = 32; ngx.say(ngx.var.a)';\n       content_by_lua 'ngx.exit(ngx.OK)';\n       add_header Foo $a;\n   }\n--- request\nGET /set\n--- response_headers\nFoo: 32\n--- response_body\n32\n\n\n\n=== TEST 17: nginx quote sql string 1\n--- config\n location /set {\n       set $a 'hello\\n\\r\\'\"\\\\'; # this runs after rewrite_by_lua\n       rewrite_by_lua 'ngx.say(ngx.quote_sql_str(ngx.var.a))';\n       content_by_lua 'ngx.exit(ngx.OK)';\n   }\n--- request\nGET /set\n--- response_body\n'hello\\n\\r\\'\\\"\\\\'\n\n\n\n=== TEST 18: nginx quote sql string 2\n--- config\nlocation /set {\n    #set $a \"hello\\n\\r'\\\"\\\\\";\n    rewrite_by_lua 'ngx.say(ngx.quote_sql_str(\"hello\\\\n\\\\r\\'\\\\\"\\\\\\\\\"))';\n    content_by_lua 'ngx.exit(ngx.OK)';\n}\n--- request\nGET /set\n--- response_body\n'hello\\n\\r\\'\\\"\\\\'\n\n\n\n=== TEST 19: use dollar\n--- config\nlocation /set {\n    rewrite_by_lua '\n        local s = \"hello 112\";\n        ngx.say(string.find(s, \"%d+$\"))';\n\n    content_by_lua 'ngx.exit(ngx.OK)';\n}\n--- request\nGET /set\n--- response_body\n79\n\n\n\n=== TEST 20: subrequests do not share variables of main requests by default\n--- config\nlocation /sub {\n    echo $a;\n}\nlocation /parent {\n    set $a 12;\n    rewrite_by_lua 'local res = ngx.location.capture(\"/sub\"); ngx.print(res.body)';\n    content_by_lua 'ngx.exit(ngx.OK)';\n}\n--- request\nGET /parent\n--- response_body eval: \"\\n\"\n\n\n\n=== TEST 21: subrequests can share variables of main requests\n--- config\nlocation /sub {\n    echo $a;\n}\nlocation /parent {\n    set $a '';\n    rewrite_by_lua '\n        ngx.var.a = 12;\n        local res = ngx.location.capture(\n            \"/sub\",\n            { share_all_vars = true }\n        );\n        ngx.print(res.body)\n    ';\n    content_by_lua 'ngx.exit(ngx.OK)';\n}\n--- request\nGET /parent\n--- response_body\n12\n\n\n\n=== TEST 22: main requests use subrequests' variables\n--- config\nlocation /sub {\n    set $a 12;\n}\nlocation /parent {\n    rewrite_by_lua '\n        local res = ngx.location.capture(\"/sub\", { share_all_vars = true });\n        ngx.say(ngx.var.a)\n    ';\n\n    content_by_lua 'ngx.exit(ngx.OK)';\n}\n--- request\nGET /parent\n--- response_body\n12\n\n\n\n=== TEST 23: main requests do NOT use subrequests' variables\n--- config\nlocation /sub {\n    set $a 12;\n    content_by_lua return;\n}\n\nlocation /parent {\n    rewrite_by_lua '\n        local res = ngx.location.capture(\"/sub\", { share_all_vars = false });\n        ngx.say(ngx.var.a)\n    ';\n    content_by_lua return;\n}\n--- request\nGET /parent\n--- response_body_like eval: \"\\n\"\n\n\n\n=== TEST 24: capture location headers\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo \"hello, world\";\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/other\");\n            ngx.say(\"type: \", res.header[\"Content-Type\"]);\n        ';\n\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\ntype: foo/bar\n\n\n\n=== TEST 25: capture location headers\n--- config\n    location /other {\n        default_type 'foo/bar';\n        rewrite_by_lua '\n            ngx.header.Bar = \"Bah\";\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/other\");\n            ngx.say(\"type: \", res.header[\"Content-Type\"]);\n            ngx.say(\"Bar: \", res.header[\"Bar\"]);\n        ';\n\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\ntype: foo/bar\nBar: Bah\n\n\n\n=== TEST 26: capture location headers\n--- config\n    location /other {\n        default_type 'foo/bar';\n        rewrite_by_lua '\n            ngx.header.Bar = \"Bah\";\n            ngx.header.Bar = nil;\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/other\");\n            ngx.say(\"type: \", res.header[\"Content-Type\"]);\n            ngx.say(\"Bar: \", res.header[\"Bar\"] or \"nil\");\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\ntype: foo/bar\nBar: nil\n\n\n\n=== TEST 27: rewrite_by_lua runs before ngx_access\n--- config\n    location /lua {\n        deny all;\n\n        rewrite_by_lua '\n            ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 28: rewrite_by_lua shouldn't send headers automatically (on simple return)\n--- config\n    location /lua {\n        rewrite_by_lua 'return';\n\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n\n    location = /foo {\n        default_type 'text/css';\n        add_header Bar Baz;\n        echo foo;\n    }\n--- request\nGET /lua\n--- response_headers\nBar: Baz\nContent-Type: text/css\n--- response_body\nfoo\n\n\n\n=== TEST 29: rewrite_by_lua shouldn't send headers automatically (on simple exit)\n--- config\n    location /lua {\n        rewrite_by_lua 'ngx.exit(ngx.OK)';\n\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n\n    location = /foo {\n        default_type 'text/css';\n        add_header Bar Baz;\n        echo foo;\n    }\n--- request\nGET /lua\n--- response_headers\nBar: Baz\nContent-Type: text/css\n--- response_body\nfoo\n\n\n\n=== TEST 30: short circuit\n--- config\n    location /lua {\n        rewrite_by_lua '\n            ngx.say(\"Hi\")\n            ngx.eof()\n            ngx.exit(ngx.HTTP_OK)\n        ';\n\n        content_by_lua '\n            print(\"HERE\")\n            ngx.print(\"BAD\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nHi\n\n\n\n=== TEST 31: nginx vars in script path\n--- config\n    location ~ /lua/(.+)$ {\n        rewrite_by_lua_file html/$1.lua;\n\n        content_by_lua '\n            print(\"HERE\")\n            ngx.print(\"BAD\")\n        ';\n    }\n--- user_files\n>>> hi.lua\nngx.say(\"Hi\")\nngx.eof()\nngx.exit(ngx.HTTP_OK)\n--- request\nGET /lua/hi\n--- response_body\nHi\n\n\n\n=== TEST 32: phase postponing works for various locations\n--- config\n    location ~ '^/lua/(.+)' {\n        set $path $1;\n        rewrite_by_lua 'ngx.say(ngx.var.path)';\n        content_by_lua return;\n    }\n    location ~ '^/lua2/(.+)' {\n        set $path $1;\n        rewrite_by_lua 'ngx.say(ngx.var.path)';\n        content_by_lua return;\n    }\n    location /main {\n        echo_location /lua/foo;\n        echo_location /lua/bar;\n        echo_location /lua2/baz;\n        echo_location /lua2/bah;\n    }\n--- request\nGET /main\n--- response_body\nfoo\nbar\nbaz\nbah\n\n\n\n=== TEST 33: server rewrite_by_lua\n--- config\n    rewrite_by_lua 'ngx.header[\"X-Foo\"] = \"bar\" -- ngx.send_headers()';\n--- request\nGET /\n--- response_body chop\n<html><head><title>It works!</title></head><body>It works!</body></html>\n--- response_headers\nX-Foo: bar\n--- no_error_log\n[error]\n\n\n\n=== TEST 34: server rewrite_by_lua_file\n--- config\n    rewrite_by_lua_file html/foo.lua;\n--- user_files\n>>> foo.lua\nngx.header[\"X-Foo\"] = \"bar\" -- ngx.send_headers()\n--- request\nGET /\n--- response_body chop\n<html><head><title>It works!</title></head><body>It works!</body></html>\n--- response_headers\nX-Foo: bar\n\n\n\n=== TEST 35: rewrite last before rewrite_by_lua\n--- config\n    location /main {\n        rewrite ^/main/xyz\\.html$ /abc.html last;\n        rewrite_by_lua 'ngx.exit(503)';\n    }\n    location ~ /abc.html {\n        echo abc;\n    }\n--- request\n    GET /main/xyz.html\n--- response_body\nabc\n\n\n\n=== TEST 36: rewrite last before rewrite_by_lua_file\n--- config\n    location /main {\n        rewrite ^/main/xyz\\.html$ /abc.html last;\n        rewrite_by_lua_file html/exit.lua;\n    }\n    location ~ /abc.html {\n        echo abc;\n    }\n--- user_files\n>>> exit.lua\nngx.exit(503)\n--- request\n    GET /main/xyz.html\n--- response_body\nabc\n\n\n\n=== TEST 37: rewrite before rewrite_by_lua\n--- config\n    location /main {\n        rewrite ^/main/xyz\\.html$ /abc.html;\n        rewrite_by_lua 'ngx.exit(503)';\n    }\n    location ~ /abc.html {\n        echo abc;\n    }\n--- request\n    GET /main/xyz.html\n--- response_body\nabc\n\n\n\n=== TEST 38: rewrite break before rewrite_by_lua\n--- config\n    location /main {\n        rewrite ^/main/xyz\\.html$ /abc.html break;\n        rewrite_by_lua 'ngx.exit(503)';\n    }\n    location ~ /abc.html {\n        echo abc;\n    }\n--- request\n    GET /main/xyz.html\n--- response_body_like: 503 Service Temporarily Unavailable\n--- error_code: 503\n\n\n\n=== TEST 39: Lua file does not exist\n--- config\n    location /lua {\n        rewrite_by_lua_file html/test2.lua;\n    }\n--- user_files\n>>> test.lua\nv = ngx.var[\"request_uri\"]\nngx.print(\"request_uri: \", v, \"\\n\")\n--- request\nGET /lua?a=1&b=2\n--- response_body_like: 404 Not Found\n--- error_code: 404\n--- error_log eval\nqr/failed to load external Lua file \".*?\\btest2\\.lua\": cannot open .*? No such file or directory/\n\n\n\n=== TEST 40: use of ngx.say() in rewrite_by_lua without exiting with 200+.\n--- config\n    location /t {\n        rewrite_by_lua \"ngx.say('test')\";\n        echo_exec /t2;\n    }\n--- request\n    GET /t\n--- response_body\ntest\n--- no_error_log\n[alert]\n\n\n\n=== TEST 41: use of ngx.say() in rewrite_by_lua without exiting with 200+. (with explicit ngx.eof())\n--- config\n    location /t {\n        rewrite_by_lua \"ngx.say('test') ngx.eof()\";\n        echo_exec /t2;\n    }\n--- request\n    GET /t\n--- response_body\ntest\n--- no_error_log\n[alert]\n\n\n\n=== TEST 42: use of ngx.say() in rewrite_by_lua without exiting with 200+. (with IO)\n--- config\n    location /t {\n        rewrite_by_lua \"ngx.say('test') ngx.sleep(0.001)\";\n        echo_exec /t2;\n    }\n--- request\n    GET /t\n--- response_body\ntest\n--- no_error_log\n[alert]\n"
  },
  {
    "path": "t/023-rewrite/sleep.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\nlog_level('debug');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 33;\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sleep 0.5\n--- config\n    location /test {\n        rewrite_by_lua '\n            ngx.update_time()\n            local before = ngx.now()\n            ngx.sleep(0.5)\n            local now = ngx.now()\n            ngx.say(now - before)\n            ngx.exit(200)\n        ';\n    }\n--- request\nGET /test\n--- response_body_like chop\n^0\\.(?:4[5-9]\\d*|5[0-9]\\d*|5)$\n--- error_log\nlua ready to sleep for\nlua sleep timer expired: \"/test?\"\n\n\n\n=== TEST 2: sleep ag\n--- config\n    location /test {\n        rewrite_by_lua '\n            ngx.update_time()\n            local before = ngx.now()\n            ngx.sleep(\"a\")\n            local now = ngx.now()\n            ngx.say(now - before)\n            ngx.exit(200)\n        ';\n    }\n--- request\nGET /test\n--- error_code: 500\n--- response_body_like: 500 Internal Server Error\n--- error_log\nbad argument #1 to 'sleep'\n\n\n\n=== TEST 3: sleep 0.5 in subrequest\n--- config\n    location /test {\n        rewrite_by_lua '\n            ngx.update_time()\n            local before = ngx.now()\n            ngx.location.capture(\"/sleep\")\n            local now = ngx.now()\n            local delay = now - before\n            ngx.say(delay)\n            ngx.exit(200)\n        ';\n    }\n    location /sleep {\n        rewrite_by_lua 'ngx.sleep(0.5) ngx.exit(200)';\n    }\n--- request\nGET /test\n--- response_body_like chop\n^0\\.(?:4[5-9]\\d*|5[0-9]\\d*|5)$\n--- error_log\nlua ready to sleep for\nlua sleep timer expired: \"/sleep?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: sleep a in subrequest with bad argument\n--- config\n    location /test {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/sleep\");\n            ngx.say(res.status)\n            ngx.exit(200)\n        ';\n    }\n    location /sleep {\n        rewrite_by_lua 'ngx.sleep(\"a\") ngx.exit(200)';\n    }\n--- request\nGET /test\n--- response_body\n500\n--- error_log\nbad argument #1 to 'sleep'\n\n\n\n=== TEST 5: sleep 0.5 - multi-times\n--- quic_max_idle_timeout: 1.0\n--- config\n    location /test {\n        rewrite_by_lua '\n            ngx.update_time()\n            local start = ngx.now()\n            ngx.sleep(0.3)\n            ngx.sleep(0.3)\n            ngx.sleep(0.3)\n            ngx.say(ngx.now() - start)\n            ngx.exit(200)\n        ';\n    }\n--- request\nGET /test\n--- response_body_like chop\n^0\\.(?:8[5-9]\\d*|9[0-9]\\d*|9)$\n--- error_log\nlua ready to sleep for\nlua sleep timer expired: \"/test?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: sleep 0.5 - interleaved by ngx.say() - ended by ngx.sleep\n--- quic_max_idle_timeout: 2.05\n--- config\n    location /test {\n        rewrite_by_lua '\n            ngx.send_headers()\n            -- ngx.location.capture(\"/sleep\")\n            ngx.sleep(1)\n            ngx.say(\"blah\")\n            ngx.sleep(1)\n            -- ngx.location.capture(\"/sleep\")\n            ngx.exit(200)\n        ';\n    }\n    location = /sleep {\n        echo_sleep 0.1;\n    }\n--- request\nGET /test\n--- response_body\nblah\n--- error_log\nlua ready to sleep\nlua sleep timer expired: \"/test?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: sleep 0.5 - interleaved by ngx.say() - not ended by ngx.sleep\n--- quic_max_idle_timeout: 0.85\n--- config\n    location /test {\n        rewrite_by_lua '\n            ngx.send_headers()\n            -- ngx.location.capture(\"/sleep\")\n            ngx.sleep(0.3)\n            ngx.say(\"blah\")\n            ngx.sleep(0.5)\n            -- ngx.location.capture(\"/sleep\")\n            ngx.say(\"hiya\")\n            ngx.exit(200)\n        ';\n    }\n    location = /sleep {\n        echo_sleep 0.1;\n    }\n--- request\nGET /test\n--- response_body\nblah\nhiya\n--- error_log\nlua ready to sleep for\nlua sleep timer expired: \"/test?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: ngx.location.capture before and after ngx.sleep\n--- config\n    location /test {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n\n            ngx.sleep(0.1)\n\n            res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n            ngx.exit(200)\n        ';\n    }\n    location = /hello {\n        echo hello world;\n    }\n    location = /sub {\n        proxy_pass http://127.0.0.1:$server_port/hello;\n    }\n--- request\nGET /test\n--- response_body\nhello world\nhello world\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/023-rewrite/socket-keepalive.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 5 + 4);\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir;\n#$ENV{TEST_NGINX_REDIS_PORT} ||= 6379;\n\n$ENV{LUA_PATH} ||=\n    '/usr/local/openresty-debug/lualib/?.lua;/usr/local/openresty/lualib/?.lua;;';\n\nno_long_string();\n#no_diff();\n#log_level 'warn';\n\nno_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        rewrite_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go(port)\n            test.go(port)\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- request\nGET /t\n--- response_body_like\n^connected: 1, reused: \\d+\nrequest sent: 11\nreceived: OK\nconnected: 1, reused: [1-9]\\d*\nrequest sent: 11\nreceived: OK\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive: free connection pool for \"]\n--- grep_error_log eval\nqr/lua tcp socket get keepalive peer: using connection|lua tcp socket keepalive create connection pool for key \"[^\"]+\"/\n\n--- grep_error_log_out eval\n[\nqq{lua tcp socket keepalive create connection pool for key \"127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}\"\nlua tcp socket get keepalive peer: using connection\n},\n\"lua tcp socket get keepalive peer: using connection\nlua tcp socket get keepalive peer: using connection\n\"]\n\n\n\n=== TEST 2: free up the whole connection pool if no active connections\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        rewrite_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go(port, true)\n            test.go(port, false)\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port, keepalive)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    if keepalive then\n        local ok, err = sock:setkeepalive()\n        if not ok then\n            ngx.say(\"failed to set reusable: \", err)\n        end\n\n    else\n        sock:close()\n    end\nend\n--- response_body_like\n^connected: 1, reused: \\d+\nrequest sent: 11\nreceived: OK\nconnected: 1, reused: [1-9]\\d*\nrequest sent: 11\nreceived: OK\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket get keepalive peer: using connection\",\n\"lua tcp socket keepalive: free connection pool for \"]\n\n\n\n=== TEST 3: upstream sockets close prematurely\n--- no_http3\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n   server_tokens off;\n   keepalive_timeout 100ms;\n   location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        rewrite_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, err = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket keepalive close handler\",\n\"lua tcp socket keepalive: free connection pool for \"]\n--- timeout: 3\n\n\n\n=== TEST 4: http keepalive\n--- no_http3\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n   server_tokens off;\n   location /t {\n        keepalive_timeout 60s;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        rewrite_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, err = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive close handler: fd:\",\n\"lua tcp socket keepalive: free connection pool for \"]\n--- timeout: 4\n\n\n\n=== TEST 5: lua_socket_keepalive_timeout\n--- quic_max_idle_timeout: 1.1\n--- config\n   server_tokens off;\n   location /t {\n       keepalive_timeout 60s;\n       lua_socket_keepalive_timeout 100ms;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        rewrite_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, res = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket keepalive close handler\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket keepalive timeout: 100 ms\",\nqr/lua tcp socket connection pool size: 30\\b/]\n--- timeout: 4\n\n\n\n=== TEST 6: lua_socket_pool_size\n--- quic_max_idle_timeout: 1.1\n--- config\n   server_tokens off;\n   location /t {\n       keepalive_timeout 60s;\n       lua_socket_keepalive_timeout 100ms;\n       lua_socket_pool_size 1;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        rewrite_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, res = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket keepalive close handler\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket keepalive timeout: 100 ms\",\nqr/lua tcp socket connection pool size: 1\\b/]\n--- timeout: 4\n\n\n\n=== TEST 7: \"lua_socket_keepalive_timeout 0\" means unlimited\n--- quic_max_idle_timeout: 1.2\n--- config\n   server_tokens off;\n   location /t {\n       keepalive_timeout 60s;\n       lua_socket_keepalive_timeout 0;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        rewrite_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, res = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- grep_error_log eval\nqr/lua tcp socket keepalive timeout: unlimited|lua tcp socket connection pool size: \\d+/\n--- grep_error_log_out eval\n[\"lua tcp socket connection pool size: 30\nlua tcp socket keepalive timeout: unlimited\n\",\n\"lua tcp socket keepalive timeout: unlimited\n\"]\n--- timeout: 4\n\n\n\n=== TEST 8: setkeepalive(timeout) overrides lua_socket_keepalive_timeout\n--- quic_max_idle_timeout: 1.1\n--- config\n   server_tokens off;\n   location /t {\n        keepalive_timeout 60s;\n        lua_socket_keepalive_timeout 60s;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        rewrite_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, res = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive(123)\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket keepalive close handler\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket keepalive timeout: 123 ms\",\nqr/lua tcp socket connection pool size: 30\\b/]\n--- timeout: 4\n\n\n\n=== TEST 9: sock:setkeepalive(timeout, size) overrides lua_socket_pool_size\n--- quic_max_idle_timeout: 1.1\n--- config\n   server_tokens off;\n   location /t {\n       keepalive_timeout 60s;\n       lua_socket_keepalive_timeout 100ms;\n       lua_socket_pool_size 100;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        rewrite_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, res = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive(101, 25)\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket keepalive close handler\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket keepalive timeout: 101 ms\",\nqr/lua tcp socket connection pool size: 25\\b/]\n--- timeout: 4\n\n\n\n=== TEST 10: sock:keepalive_timeout(0) means unlimited\n--- quic_max_idle_timeout: 1.1\n--- config\n   server_tokens off;\n   location /t {\n       keepalive_timeout 60s;\n       lua_socket_keepalive_timeout 1000ms;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        rewrite_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, res = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive(0)\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- grep_error_log eval\nqr/lua tcp socket keepalive timeout: unlimited|lua tcp socket connection pool size: \\d+/\n--- grep_error_log_out eval\n[\"lua tcp socket connection pool size: 30\nlua tcp socket keepalive timeout: unlimited\n\",\n\"lua tcp socket keepalive timeout: unlimited\n\"\n]\n--- timeout: 4\n\n\n\n=== TEST 11: sanity (uds)\n--- http_config eval\n\"\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n    server {\n        listen unix:$::HtmlDir/nginx.sock;\n        default_type 'text/plain';\n\n        server_tokens off;\n        location /foo {\n            echo foo;\n            more_clear_headers Date;\n        }\n    }\n\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        rewrite_by_lua '\n            local test = require \"test\"\n            local path = \"$TEST_NGINX_HTML_DIR/nginx.sock\";\n            local port = ngx.var.port\n            test.go(path, port)\n            test.go(path, port)\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(path, port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"unix:\" .. path)\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"GET /foo HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: keepalive\\r\\n\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local reader = sock:receiveuntil(\"\\r\\n0\\r\\n\\r\\n\")\n    local data, err = reader()\n\n    if not data then\n        ngx.say(\"failed to receive response body: \", err)\n        return\n    end\n\n    ngx.say(\"received response of \", #data, \" bytes\")\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- response_body_like\n^connected: 1, reused: \\d+\nrequest sent: 61\nreceived response of 119 bytes\nconnected: 1, reused: [1-9]\\d*\nrequest sent: 61\nreceived response of 119 bytes\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive: free connection pool for \"]\n--- grep_error_log eval\nqr/lua tcp socket get keepalive peer: using connection|lua tcp socket keepalive create connection pool for key \"unix:/\n--- grep_error_log_out eval\n[qq{lua tcp socket keepalive create connection pool for key \"unix:\nlua tcp socket get keepalive peer: using connection\n},\n\"lua tcp socket get keepalive peer: using connection\nlua tcp socket get keepalive peer: using connection\n\"\n]\n\n\n\n=== TEST 12: github issue #108: ngx.location.capture + redis.set_keepalive\n--- http_config eval\n    qq{\n        lua_package_path \"$::HtmlDir/?.lua;;\";\n    }\n--- config\n    location /t {\n        default_type text/html;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        #lua_code_cache off;\n        lua_need_request_body on;\n        rewrite_by_lua_file html/t.lua;\n        content_by_lua return;\n    }\n\n    location /anyurl {\n        internal;\n        proxy_pass http://127.0.0.1:$server_port/dummy;\n    }\n\n    location = /dummy {\n        echo dummy;\n    }\n--- user_files\n>>> t.lua\nlocal sock, err = ngx.socket.connect(\"127.0.0.1\", ngx.var.port)\nif not sock then ngx.say(err) return end\nsock:send(\"flush_all\\r\\n\")\nsock:receive()\nsock:setkeepalive()\n\nsock, err = ngx.socket.connect(\"127.0.0.1\", ngx.var.port)\nif not sock then ngx.say(err) return end\nlocal res = ngx.location.capture(\"/anyurl\") --3\n\nngx.say(\"ok\")\n--- request\n    GET /t\n--- response_body\nok\n--- error_log\nlua tcp socket get keepalive peer: using connection\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 13: github issue #110: ngx.exit with HTTP_NOT_FOUND causes worker process to exit\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    error_page 404 /404.html;\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        access_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go(port)\n            ngx.exit(404)\n        ';\n        echo hello;\n    }\n--- user_files\n>>> 404.html\nNot found, dear...\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        ngx.log(ngx.ERR, \"failed to connect: \", err)\n        return\n    end\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.log(ngx.ERR, \"failed to send request: \", err)\n        return\n    end\n\n    local line, err, part = sock:receive()\n    if not line then\n        ngx.log(ngx.ERR, \"failed to receive a line: \", err, \" [\", part, \"]\")\n        return\n    end\n\n    -- local ok, err = sock:setkeepalive()\n    -- if not ok then\n        -- ngx.log(ngx.ERR, \"failed to set reusable: \", err)\n        -- return\n    -- end\nend\n--- request\nGET /t\n--- response_body\nNot found, dear...\n--- error_code: 404\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/023-rewrite/subrequest.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 2);\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: DELETE\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_DELETE });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nDELETE\n\n\n\n=== TEST 2: DELETE (proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_DELETE });\n\n            ngx.print(res.body)\n        ';\n\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nDELETE\n\n\n\n=== TEST 3: POST (nobody, proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_POST });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nPOST\n\n\n\n=== TEST 4: HEAD\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_HEAD });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\n\n\n\n=== TEST 5: explicit GET\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_GET });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nGET\n\n\n\n=== TEST 6: implicit GET\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\")\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nGET\n\n\n\n=== TEST 7: implicit GET (empty option table)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\", {})\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nGET\n\n\n\n=== TEST 8: PUT (nobody, proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo_read_request_body;\n\n        echo $echo_request_method;\n        echo_request_body;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body chomp\nPUT\nhello\n\n\n\n=== TEST 9: PUT (nobody, no proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        #echo_read_request_body;\n\n        echo $echo_request_method;\n        #echo $echo_request_body;\n        echo_request_body;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body chomp\nPUT\nhello\n\n\n\n=== TEST 10: PUT (nobody, no proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        #echo_read_request_body;\n\n        echo $echo_request_method;\n        #echo $echo_request_body;\n        echo_request_body;\n        #echo \"[$http_content_length]\";\n        echo;\n    }\n\n    location /foo {\n        echo $echo_request_method;\n        echo -n \"[$http_content_length]\";\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n\n            ngx.print(res.body)\n\n            res = ngx.location.capture(\"/foo\")\n            ngx.say(res.body)\n\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nPUT\nhello\nGET\n[]\n\n\n\n=== TEST 11: POST (with body, proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo_read_request_body;\n\n        echo $echo_request_method;\n        echo_request_body;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_POST, body = \"hello\" });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body chomp\nPOST\nhello\n\n\n\n=== TEST 12: POST (with body, memc method)\n--- config\n    location /flush {\n        set $memc_cmd flush_all;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /memc {\n        set $memc_key $echo_request_uri;\n        set $memc_exptime 600;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            ngx.location.capture(\"/flush\");\n\n            local res = ngx.location.capture(\"/memc\");\n            ngx.say(\"GET: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n            ngx.say(\"PUT: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\");\n            ngx.say(\"cached: \" .. res.body);\n\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nGET: 404\nPUT: 201\ncached: hello\n\n\n\n=== TEST 13: POST (with body, memc method)\n--- config\n    location /flush {\n        set $memc_cmd flush_all;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /memc {\n        set $memc_cmd \"\";\n        set $memc_key $echo_request_uri;\n        set $memc_exptime 600;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            ngx.location.capture(\"/flush\",\n                { share_all_vars = true });\n\n            local res = ngx.location.capture(\"/memc\",\n                { share_all_vars = true });\n            ngx.say(\"GET: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello\", share_all_vars = true });\n            ngx.say(\"PUT: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\", { share_all_vars = true });\n            ngx.say(\"cached: \" .. res.body);\n\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nGET: 404\nPUT: 201\ncached: hello\n\n\n\n=== TEST 14: empty args option table\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = {} })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body eval: \"\\n\"\n\n\n\n=== TEST 15: non-empty args option table (1 pair)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { [\"fo=\"] = \"=>\" } })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nfo%3D=%3D%3E\n\n\n\n=== TEST 16: non-empty args option table (2 pairs)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { [\"fo=\"] = \"=>\",\n                    [\"=\"] = \":\" } })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body_like chop\n^(?:fo%3D=%3D%3E\\&%3D=%3A|%3D=%3A\\&fo%3D=%3D%3E)$\n\n\n\n=== TEST 17: non-empty args option table (2 pairs, no special chars)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { foo = 3,\n                    bar = \"hello\" } })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body_like chop\n^(?:bar=hello\\&foo=3|foo=3\\&bar=hello)$\n\n\n\n=== TEST 18: non-empty args option table (number key)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { [57] = \"hi\" } })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 19: non-empty args option table (plain arrays)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { \"hi\" } })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 20: more args\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo?a=3\",\n                { args = { b = 4 } })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\na=3&b=4\n\n\n\n=== TEST 21: more args\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/foo?a=3\",\n                { args = \"b=4\" })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\na=3&b=4\n\n\n\n=== TEST 22: more args\n--- config\n    location /memc {\n        set $memc_cmd get;\n        set $memc_key $arg_key;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /memc_set {\n        #set $memc_cmd set;\n        #set $memc_key $arg_key;\n        #memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n        echo OK;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            print(\"HELLO\")\n            local memc_key = \"hello\"\n            local res = ngx.location.capture(\"/memc?key=\" .. memc_key )\n            ngx.say(\"copass: res \" .. res.status)\n\n            if res.status == 404 then\n                   ngx.say(\"copas: capture /memc_set\")\n                   res = ngx.location.capture(\"/memc_set?key=\" .. memc_key)\n                   ngx.say(\"copss: status \" .. res.status);\n            end\n        ';\n        content_by_lua 'return';\n        #echo Hi;\n    }\n--- request\n    GET /lua\n--- response_body\ncopass: res 404\ncopas: capture /memc_set\ncopss: status 200\n\n\n\n=== TEST 23: I/O in named location\nthe nginx core requires the patch https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.0.15-reset_wev_handler_in_named_locations.patch\n--- config\n    location /t {\n        echo_exec @named;\n    }\n\n    location @named {\n        rewrite_by_lua '\n            ngx.location.capture(\"/hello\")\n        ';\n        echo done;\n    }\n\n    location /hello {\n        echo hello;\n    }\n--- request\n    GET /t\n--- response_body\ndone\n"
  },
  {
    "path": "t/023-rewrite/tcp-socket-timeout.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nBEGIN {\n    if (!defined $ENV{LD_PRELOAD}) {\n        $ENV{LD_PRELOAD} = '';\n    }\n\n    if ($ENV{LD_PRELOAD} !~ /\\bmockeagain\\.so\\b/) {\n        $ENV{LD_PRELOAD} = \"mockeagain.so $ENV{LD_PRELOAD}\";\n    }\n\n    if ($ENV{MOCKEAGAIN} eq 'r') {\n        $ENV{MOCKEAGAIN} = 'rw';\n\n    } else {\n        $ENV{MOCKEAGAIN} = 'w';\n    }\n\n    delete($ENV{TEST_NGINX_USE_HTTP2});\n    $ENV{TEST_NGINX_EVENT_TYPE} = 'poll';\n    $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'get helloworld';\n}\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4 + 8);\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\nno_long_string();\nno_diff();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: lua_socket_connect_timeout only\n--- config\n    server_tokens off;\n    lua_socket_connect_timeout 100ms;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t1 {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t1\n--- response_body\nfailed to connect: timeout\n--- error_log\nlua tcp socket connect timeout: 100\nlua tcp socket connect timed out, upstream: 127.0.0.2:12345(127.0.0.2)\n--- timeout: 10\n\n\n\n=== TEST 2: sock:settimeout() overrides lua_socket_connect_timeout\n--- config\n    server_tokens off;\n    lua_socket_connect_timeout 60s;\n    lua_socket_log_errors off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t2 {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(150)\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t2\n--- response_body\nfailed to connect: timeout\n--- error_log\nlua tcp socket connect timeout: 150\n--- no_error_log\n[error]\n[alert]\n--- timeout: 10\n\n\n\n=== TEST 3: sock:settimeout(nil) does not override lua_socket_connect_timeout\n--- config\n    server_tokens off;\n    lua_socket_log_errors off;\n    lua_socket_connect_timeout 102ms;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    #resolver_timeout 3s;\n    location /t3 {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(nil)\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t3\n--- response_body\nfailed to connect: timeout\n--- error_log\nlua tcp socket connect timeout: 102\n--- no_error_log\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 4: sock:settimeout(0) does not override lua_socket_connect_timeout\n--- config\n    server_tokens off;\n    lua_socket_connect_timeout 102ms;\n    lua_socket_log_errors off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t4 {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(0)\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t4\n--- response_body\nfailed to connect: timeout\n--- error_log\nlua tcp socket connect timeout: 102\n--- timeout: 5\n--- no_error_log\n[error]\n[alert]\n--- timeout: 10\n\n\n\n=== TEST 5: -1 is bad timeout value\n--- config\n    server_tokens off;\n    lua_socket_connect_timeout 102ms;\n    lua_socket_log_errors off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t5 {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(-1)\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t5\n--- response_body_like chomp\n500 Internal Server Error\n--- error_log\nbad timeout value\n--- timeout: 10\n--- error_code: 500\n\n\n\n=== TEST 6: lua_socket_read_timeout only\n--- config\n    server_tokens off;\n    lua_socket_read_timeout 100ms;\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local line\n            line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: timeout\n--- error_log\nlua tcp socket read timeout: 100\nlua tcp socket connect timeout: 60000\nlua tcp socket read timed out\n\n\n\n=== TEST 7: sock:settimeout() overrides lua_socket_read_timeout\n--- config\n    server_tokens off;\n    lua_socket_read_timeout 60s;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(150)\n\n            local line\n            line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: timeout\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket read timeout: 150\nlua tcp socket read timed out\n\n\n\n=== TEST 8: sock:settimeout(nil) does not override lua_socket_read_timeout\n--- config\n    server_tokens off;\n    lua_socket_read_timeout 102ms;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(nil)\n\n            local line\n            line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: timeout\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket read timeout: 102\nlua tcp socket read timed out\n\n\n\n=== TEST 9: sock:settimeout(0) does not override lua_socket_read_timeout\n--- config\n    server_tokens off;\n    lua_socket_read_timeout 102ms;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(0)\n\n            local line\n            line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: timeout\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket read timeout: 102\nlua tcp socket read timed out\n\n\n\n=== TEST 10: -1 is bad timeout value\n--- config\n    server_tokens off;\n    lua_socket_read_timeout 102ms;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            sock:settimeout(-1)\n\n            local line\n            line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body_like chomp\n500 Internal Server Error\n--- error_log\nbad timeout value\n--- timeout: 10\n--- error_code: 500\n\n\n\n=== TEST 11: lua_socket_send_timeout only\n--- config\n    server_tokens off;\n    lua_socket_send_timeout 100ms;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local bytes\n            bytes, err = sock:send(\"get helloworld!\")\n            if bytes then\n                ngx.say(\"sent: \", bytes)\n            else\n                ngx.say(\"failed to send: \", err)\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to send: timeout\n--- error_log\nlua tcp socket send timeout: 100\nlua tcp socket connect timeout: 60000\nlua tcp socket write timed out\n\n\n\n=== TEST 12: sock:settimeout() overrides lua_socket_send_timeout\n--- config\n    server_tokens off;\n    lua_socket_send_timeout 60s;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(150)\n\n            local bytes\n            bytes, err = sock:send(\"get helloworld!\")\n            if bytes then\n                ngx.say(\"sent: \", bytes)\n            else\n                ngx.say(\"failed to send: \", err)\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to send: timeout\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket send timeout: 150\nlua tcp socket write timed out\n\n\n\n=== TEST 13: sock:settimeout(nil) does not override lua_socket_send_timeout\n--- config\n    server_tokens off;\n    lua_socket_send_timeout 102ms;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(nil)\n\n            local bytes\n            bytes, err = sock:send(\"get helloworld!\")\n            if bytes then\n                ngx.say(\"sent: \", bytes)\n            else\n                ngx.say(\"failed to send: \", err)\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to send: timeout\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket send timeout: 102\nlua tcp socket write timed out\n\n\n\n=== TEST 14: sock:settimeout(0) does not override lua_socket_send_timeout\n--- config\n    server_tokens off;\n    lua_socket_send_timeout 102ms;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(0)\n\n            local bytes\n            bytes, err = sock:send(\"get helloworld!\")\n            if bytes then\n                ngx.say(\"sent: \", bytes)\n            else\n                ngx.say(\"failed to send: \", err)\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to send: timeout\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket send timeout: 102\nlua tcp socket write timed out\n\n\n\n=== TEST 15: -1 is bad timeout value\n--- config\n    server_tokens off;\n    lua_socket_send_timeout 102ms;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            sock:settimeout(-1)\n\n            local bytes\n            bytes, err = sock:send(\"get helloworld!\")\n            if bytes then\n                ngx.say(\"sent: \", bytes)\n            else\n                ngx.say(\"failed to send: \", err)\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body_like chomp\n500 Internal Server Error\n--- error_log\nbad timeout value\n--- timeout: 10\n--- error_code: 500\n"
  },
  {
    "path": "t/023-rewrite/tcp-socket.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 107;\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\n#log_level 'warn';\n\n#no_long_string();\n#no_diff();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 2: no trailing newline\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 1234;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            sock:close()\n            ngx.say(\"closed\")\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.print(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 3\nreceived: Connection: close\nreceived: \nfailed to receive a line: closed [foo]\nclosed\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 3: no resolver defined\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 1234;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"agentzh.org\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nfailed to connect: no resolver defined to resolve \"agentzh.org\"\nconnected: nil\nfailed to send request: closed\n--- error_log\nattempt to send data on a closed socket:\n--- no_http2\n\n\n\n=== TEST 4: with resolver\n--- timeout: 10\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = 80\n            local ok, err = sock:connect(\"agentzh.org\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET / HTTP/1.0\\\\r\\\\nHost: agentzh.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local line, err = sock:receive()\n            if line then\n                ngx.say(\"first line received: \", line)\n\n            else\n                ngx.say(\"failed to receive the first line: \", err)\n            end\n\n            line, err = sock:receive()\n            if line then\n                ngx.say(\"second line received: \", line)\n\n            else\n                ngx.say(\"failed to receive the second line: \", err)\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body_like\nconnected: 1\nrequest sent: 56\nfirst line received: HTTP\\/1\\.1 200 OK\nsecond line received: (?:Date|Server): .*?\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: connection refused (tcp)\n--- config\n    location /test {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", 16787)\n            ngx.say(\"connect: \", ok, \" \", err)\n\n            local bytes\n            bytes, err = sock:send(\"hello\")\n            ngx.say(\"send: \", bytes, \" \", err)\n\n            local line\n            line, err = sock:receive()\n            ngx.say(\"receive: \", line, \" \", err)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n--- request\n    GET /test\n--- response_body\nconnect: nil connection refused\nsend: nil closed\nreceive: nil closed\nclose: nil closed\n--- error_log eval\nqr/connect\\(\\) failed \\(\\d+: Connection refused\\)/\n\n\n\n=== TEST 6: connection timeout (tcp)\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_socket_connect_timeout 100ms;\n    lua_socket_send_timeout 100ms;\n    lua_socket_read_timeout 100ms;\n    resolver_timeout 3s;\n    location /test {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            ngx.say(\"connect: \", ok, \" \", err)\n\n            local bytes\n            bytes, err = sock:send(\"hello\")\n            ngx.say(\"send: \", bytes, \" \", err)\n\n            local line\n            line, err = sock:receive()\n            ngx.say(\"receive: \", line, \" \", err)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n--- request\n    GET /test\n--- response_body\nconnect: nil timeout\nsend: nil closed\nreceive: nil closed\nclose: nil closed\n--- error_log\nlua tcp socket connect timed out, upstream: 127.0.0.2:12345(127.0.0.2)\n--- timeout: 10\n\n\n\n=== TEST 7: not closed manually\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: resolver error (host not found)\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = 80\n            local ok, err = sock:connect(\"blah-blah-not-found.agentzh.org\", port)\n            print(\"connected: \", ok, \" \", err, \" \", not ok)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET / HTTP/1.0\\\\r\\\\nHost: agentzh.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body_like\n^failed to connect: blah-blah-not-found\\.agentzh\\.org could not be resolved(?: \\(3: Host not found\\))?\nconnected: nil\nfailed to send request: closed$\n--- error_log\nattempt to send data on a closed socket\n--- timeout: 10\n\n\n\n=== TEST 9: resolver error (timeout)\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 1ms;\n    location /t {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = 80\n            local ok, err = sock:connect(\"blah-blah-not-found.agentzh.org\", port)\n            print(\"connected: \", ok, \" \", err, \" \", not ok)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET / HTTP/1.0\\\\r\\\\nHost: agentzh.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /t\n--- response_body_like\n^failed to connect: blah-blah-not-found\\.agentzh\\.org could not be resolved(?: \\(\\d+: (?:Operation timed out|Host not found)\\))?\nconnected: nil\nfailed to send request: closed$\n--- error_log\nattempt to send data on a closed socket\n\n\n\n=== TEST 10: explicit *l pattern for receive\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err = sock:receive(\"*l\")\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err)\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed\nclose: 1 nil\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 11: *a pattern for receive\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local data, err = sock:receive(\"*a\")\n            if data then\n                ngx.say(\"receive: \", data)\n                ngx.say(\"err: \", err)\n\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\n\"connected: 1\nrequest sent: 57\nreceive: HTTP/1.1 200 OK\\r\nServer: nginx\\r\nContent-Type: text/plain\\r\nContent-Length: 4\\r\nConnection: close\\r\n\\r\nfoo\n\nerr: nil\nclose: 1 nil\n\"\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 12: mixing *a and *l patterns for receive\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local line, err = sock:receive(\"*l\")\n            if line then\n                ngx.say(\"receive: \", line)\n                ngx.say(\"err: \", err)\n\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n\n            local data\n            data, err = sock:receive(\"*a\")\n            if data then\n                ngx.say(\"receive: \", data)\n                ngx.say(\"err: \", err)\n\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\n\"connected: 1\nrequest sent: 57\nreceive: HTTP/1.1 200 OK\nerr: nil\nreceive: Server: nginx\\r\nContent-Type: text/plain\\r\nContent-Length: 4\\r\nConnection: close\\r\n\\r\nfoo\n\nerr: nil\nclose: 1 nil\n\"\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 13: receive by chunks\n--- timeout: 5\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local data, err, partial = sock:receive(10)\n                if data then\n                    local len = string.len(data)\n                    if len == 10 then\n                        ngx.print(\"[\", data, \"]\")\n                    else\n                        ngx.say(\"ERROR: returned invalid length of data: \", len)\n                    end\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", partial, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\n\"connected: 1\nrequest sent: 57\n[HTTP/1.1 2][00 OK\\r\nSer][ver: nginx][\\r\nContent-][Type: text][/plain\\r\nCo][ntent-Leng][th: 4\\r\nCon][nection: c][lose\\r\n\\r\nfo]failed to receive a line: closed [o\n]\nclose: 1 nil\n\"\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 14: receive by chunks (very small buffer)\n--- timeout: 5\n--- config\n    server_tokens off;\n    lua_socket_buffer_size 1;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local data, err, partial = sock:receive(10)\n                if data then\n                    local len = string.len(data)\n                    if len == 10 then\n                        ngx.print(\"[\", data, \"]\")\n                    else\n                        ngx.say(\"ERROR: returned invalid length of data: \", len)\n                    end\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", partial, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\n\"connected: 1\nrequest sent: 57\n[HTTP/1.1 2][00 OK\\r\nSer][ver: nginx][\\r\nContent-][Type: text][/plain\\r\nCo][ntent-Leng][th: 4\\r\nCon][nection: c][lose\\r\n\\r\nfo]failed to receive a line: closed [o\n]\nclose: 1 nil\n\"\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 15: line reading (very small buffer)\n--- config\n    server_tokens off;\n    lua_socket_buffer_size 1;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 16: ngx.socket.connect (working)\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local port = ngx.var.port\n            local sock, err = ngx.socket.connect(\"127.0.0.1\", port)\n            if not sock then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected.\")\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            local ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected.\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 17: ngx.socket.connect() shortcut (connection refused)\n--- config\n    location /test {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local sock, err = sock:connect(\"127.0.0.1\", 16787)\n            if not sock then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes\n            bytes, err = sock:send(\"hello\")\n            ngx.say(\"send: \", bytes, \" \", err)\n\n            local line\n            line, err = sock:receive()\n            ngx.say(\"receive: \", line, \" \", err)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n--- request\n    GET /test\n--- response_body\nfailed to connect: connection refused\n--- error_log eval\nqr/connect\\(\\) failed \\(\\d+: Connection refused\\)/\n\n\n\n=== TEST 18: receive by chunks (stringified size)\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local data, err, partial = sock:receive(\"10\")\n                if data then\n                    local len = string.len(data)\n                    if len == 10 then\n                        ngx.print(\"[\", data, \"]\")\n                    else\n                        ngx.say(\"ERROR: returned invalid length of data: \", len)\n                    end\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", partial, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\n\"connected: 1\nrequest sent: 57\n[HTTP/1.1 2][00 OK\\r\nSer][ver: nginx][\\r\nContent-][Type: text][/plain\\r\nCo][ntent-Leng][th: 4\\r\nCon][nection: c][lose\\r\n\\r\nfo]failed to receive a line: closed [o\n]\nclose: 1 nil\n\"\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 19: cannot survive across request boundary (send)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        rewrite_by_lua '\n            local test = require \"test\"\n            test.go(ngx.var.port)\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction go(port)\n    if not sock then\n        sock = ngx.socket.tcp()\n        local port = ngx.var.port\n        local ok, err = sock:connect(\"127.0.0.1\", port)\n        if not ok then\n            ngx.say(\"failed to connect: \", err)\n            return\n        end\n\n        ngx.say(\"connected: \", ok)\n    end\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\nend\n--- request\nGET /t\n--- response_body_like eval\n\"^(?:connected: 1\nrequest sent: 11\nreceived: OK|failed to send request: closed)\\$\"\n\n\n\n=== TEST 20: cannot survive across request boundary (receive)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        rewrite_by_lua '\n            local test = require \"test\"\n            test.go(ngx.var.port)\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction go(port)\n    if not sock then\n        sock = ngx.socket.tcp()\n        local port = ngx.var.port\n        local ok, err = sock:connect(\"127.0.0.1\", port)\n        if not ok then\n            ngx.say(\"failed to connect: \", err)\n            return\n        end\n\n        ngx.say(\"connected: \", ok)\n\n    else\n        local line, err, part = sock:receive()\n        if line then\n            ngx.say(\"received: \", line)\n\n        else\n            ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n        end\n        return\n    end\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\nend\n--- request\nGET /t\n--- response_body_like eval\nqr/^(?:connected: 1\nrequest sent: 11\nreceived: OK|failed to receive a line: closed \\[nil\\])$/\n\n\n\n=== TEST 21: cannot survive across request boundary (close)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        rewrite_by_lua '\n            local test = require \"test\"\n            test.go(ngx.var.port)\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction go(port)\n    if not sock then\n        sock = ngx.socket.tcp()\n        local port = ngx.var.port\n        local ok, err = sock:connect(\"127.0.0.1\", port)\n        if not ok then\n            ngx.say(\"failed to connect: \", err)\n            return\n        end\n\n        ngx.say(\"connected: \", ok)\n\n    else\n        local ok, err = sock:close()\n        if ok then\n            ngx.say(\"successfully closed\")\n\n        else\n            ngx.say(\"failed to close: \", err)\n        end\n        return\n    end\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\nend\n--- request\nGET /t\n--- response_body_like eval\nqr/^(?:connected: 1\nrequest sent: 11\nreceived: OK|failed to close: closed)$/\n\n\n\n=== TEST 22: cannot survive across request boundary (connect)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        rewrite_by_lua '\n            local test = require \"test\"\n            test.go(ngx.var.port)\n            test.go(ngx.var.port)\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction go(port)\n    if not sock then\n        sock = ngx.socket.tcp()\n        local port = ngx.var.port\n        local ok, err = sock:connect(\"127.0.0.1\", port)\n        if not ok then\n            ngx.say(\"failed to connect: \", err)\n            return\n        end\n\n        ngx.say(\"connected: \", ok)\n\n    else\n        local port = ngx.var.port\n        local ok, err = sock:connect(\"127.0.0.1\", port)\n        if not ok then\n            ngx.say(\"failed to connect again: \", err)\n            return\n        end\n\n        ngx.say(\"connected again: \", ok)\n    end\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\nend\n--- request\nGET /t\n--- response_body_like eval\nqr/^(?:connected(?: again)?: 1\nrequest sent: 11\nreceived: OK\n){2}$/\n--- error_log\nlua reuse socket upstream ctx\n--- no_error_log\n[error]\n--- SKIP\n\n\n\n=== TEST 23: connect again immediately\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected again: \", ok)\n\n            local req = \"flush_all\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local line, err, part = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n\n            else\n                ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nconnected again: 1\nrequest sent: 11\nreceived: OK\nclose: 1 nil\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua reuse socket upstream\", \"lua tcp socket reconnect without shutting down\"]\n\n\n\n=== TEST 24: two sockets mix together\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port1 $TEST_NGINX_MEMCACHED_PORT;\n        set $port2 $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock1 = ngx.socket.tcp()\n            local sock2 = ngx.socket.tcp()\n\n            local port1 = ngx.var.port1\n            local port2 = ngx.var.port2\n\n            local ok, err = sock1:connect(\"127.0.0.1\", port1)\n            if not ok then\n                ngx.say(\"1: failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"1: connected: \", ok)\n\n            ok, err = sock2:connect(\"127.0.0.1\", port2)\n            if not ok then\n                ngx.say(\"2: failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"2: connected: \", ok)\n\n            local req1 = \"flush_all\\\\r\\\\n\"\n            local bytes, err = sock1:send(req1)\n            if not bytes then\n                ngx.say(\"1: failed to send request: \", err)\n                return\n            end\n            ngx.say(\"1: request sent: \", bytes)\n\n            local req2 = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            local bytes, err = sock2:send(req2)\n            if not bytes then\n                ngx.say(\"2: failed to send request: \", err)\n                return\n            end\n            ngx.say(\"2: request sent: \", bytes)\n\n            local line, err, part = sock1:receive()\n            if line then\n                ngx.say(\"1: received: \", line)\n\n            else\n                ngx.say(\"1: failed to receive a line: \", err, \" [\", part, \"]\")\n            end\n\n            line, err, part = sock2:receive()\n            if line then\n                ngx.say(\"2: received: \", line)\n\n            else\n                ngx.say(\"2: failed to receive a line: \", err, \" [\", part, \"]\")\n            end\n\n            ok, err = sock1:close()\n            ngx.say(\"1: close: \", ok, \" \", err)\n\n            ok, err = sock2:close()\n            ngx.say(\"2: close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\n1: connected: 1\n2: connected: 1\n1: request sent: 11\n2: request sent: 57\n1: received: OK\n2: received: HTTP/1.1 200 OK\n1: close: 1 nil\n2: close: 1 nil\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 25: send tables of string fragments\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = {\"GET\", \" \", \"/foo\", \" HTTP/\", 1, \".\", 0, \"\\\\r\\\\n\",\n                         \"Host: localhost\\\\r\\\\n\", \"Connection: close\\\\r\\\\n\",\n                         \"\\\\r\\\\n\"}\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 26: send tables of string fragments (bad type \"nil\")\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = {\"GET\", \" \", \"/foo\", \" HTTP/\", nil, 1, \".\", 0, \"\\\\r\\\\n\",\n                         \"Host: localhost\\\\r\\\\n\", \"Connection: close\\\\r\\\\n\",\n                         \"\\\\r\\\\n\"}\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- ignore_response\n--- error_log\nbad argument #1 to 'send' (bad data type nil found)\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP/3 stream 0 reset by server#\n--- no_http2\n\n\n\n=== TEST 27: send tables of string fragments (bad type \"boolean\")\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = {\"GET\", \" \", \"/foo\", \" HTTP/\", true, 1, \".\", 0, \"\\\\r\\\\n\",\n                         \"Host: localhost\\\\r\\\\n\", \"Connection: close\\\\r\\\\n\",\n                         \"\\\\r\\\\n\"}\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- ignore_response\n--- error_log\nbad argument #1 to 'send' (bad data type boolean found)\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP/3 stream 0 reset by server#\n--- no_http2\n\n\n\n=== TEST 28: send tables of string fragments (bad type ngx.null)\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = {\"GET\", \" \", \"/foo\", \" HTTP/\", ngx.null, 1, \".\", 0, \"\\\\r\\\\n\",\n                         \"Host: localhost\\\\r\\\\n\", \"Connection: close\\\\r\\\\n\",\n                         \"\\\\r\\\\n\"}\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- ignore_response\n--- error_log\nbad argument #1 to 'send' (bad data type userdata found)\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP/3 stream 0 reset by server#\n--- no_http2\n\n\n\n=== TEST 29: cosocket before location capture (tcpsock:send did not clear u->waiting)\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"flush_all\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local line, err, part = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n\n            else\n                ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n\n            local resp = ngx.location.capture(\"/memc\")\n            if type(resp) ~= \"table\" then\n                ngx.say(\"bad resp: type \", type(resp), \": \", resp)\n                return\n            end\n\n            ngx.print(\"subrequest: \", resp.status, \", \", resp.body)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /memc {\n        set $memc_cmd flush_all;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n--- request\nGET /t\n--- response_body eval\n\"connected: 1\nrequest sent: 11\nreceived: OK\nclose: 1 nil\nsubrequest: 200, OK\\r\n\"\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 30: CR in a line\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\\\\r\\\\rbar\\\\rbaz\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 13\nreceived: Connection: close\nreceived: \nreceived: foobarbaz\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\n[error]\n--- SKIP\n--- no_http2\n\n\n\n=== TEST 31: receive(0)\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local data, err, part = sock:receive(0)\n            if not data then\n                ngx.say(\"failed to receive(0): \", err)\n                return\n            end\n\n            ngx.say(\"receive(0): [\", data, \"]\")\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceive(0): []\nclose: 1 nil\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 32: send(\"\")\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local bytes, err = sock:send(\"\")\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"send(\\\\\"\\\\\"): \", bytes)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nsend(\"\"): 0\nclose: 1 nil\n--- no_error_log\n[error]\n--- no_http2\n\n\n\n=== TEST 33: bad request tries to connect\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        rewrite_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                test.new_sock()\n            end\n            local sock = test.get_sock()\n            local ok, err = sock:connect(\"127.0.0.1\", ngx.var.port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n            else\n                ngx.say(\"connected\")\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.tcp()\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^connected\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: rewrite_by_lua\\(nginx\\.conf:\\d+\\):7: bad request/\n\n--- no_error_log\n[alert]\n--- no_http2\n\n\n\n=== TEST 34: bad request tries to receive\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        rewrite_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:connect(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                else\n                    ngx.say(\"connected\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            sock:receive()\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.tcp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^connected\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: rewrite_by_lua\\(nginx\\.conf:\\d+\\):14: bad request/\n\n--- no_error_log\n[alert]\n--- no_http2\n\n\n\n=== TEST 35: bad request tries to send\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        rewrite_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:connect(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                else\n                    ngx.say(\"connected\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            sock:send(\"a\")\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.tcp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^connected\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: rewrite_by_lua\\(nginx\\.conf:\\d+\\):14: bad request/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 36: bad request tries to close\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        rewrite_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:connect(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                else\n                    ngx.say(\"connected\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            sock:close()\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.tcp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^connected\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: rewrite_by_lua\\(nginx\\.conf:\\d+\\):14: bad request/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 37: bad request tries to set keepalive\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        rewrite_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:connect(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                else\n                    ngx.say(\"connected\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            sock:setkeepalive()\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.tcp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^connected\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: rewrite_by_lua\\(nginx\\.conf:\\d+\\):14: bad request/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 38: bad request tries to receiveuntil\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        rewrite_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:connect(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                else\n                    ngx.say(\"connected\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            local it, err = sock:receiveuntil(\"abc\")\n            if it then\n                it()\n            end\n        ';\n\n        content_by_lua return;\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.tcp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^connected\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: rewrite_by_lua\\(nginx\\.conf:\\d+\\):16: bad request/\n\n--- no_error_log\n[alert]\n"
  },
  {
    "path": "t/023-rewrite/unix-socket.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (2 * blocks() + 1);\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n\nno_long_string();\n#no_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: connection refused (unix domain socket)\n--- config\n    location /test {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"unix:/tmp/nosuchfile.sock\")\n            ngx.say(\"connect: \", ok, \" \", err)\n\n            local bytes\n            bytes, err = sock:send(\"hello\")\n            ngx.say(\"send: \", bytes, \" \", err)\n\n            local line\n            line, err = sock:receive()\n            ngx.say(\"receive: \", line, \" \", err)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n--- request\n    GET /test\n--- response_body\nconnect: nil no such file or directory\nsend: nil closed\nreceive: nil closed\nclose: nil closed\n--- error_log eval\nqr{\\[crit\\] .*? connect\\(\\) to unix:/tmp/nosuchfile\\.sock failed}\n\n\n\n=== TEST 2: invalid host argument\n--- http_server\n    server {\n        listen /tmp/test-nginx.sock;\n        default_type 'text/plain';\n\n        server_tokens off;\n        location /foo {\n            content_by_lua 'ngx.say(\"foo\")';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /test {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"/tmp/test-nginx.sock\")\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n\n        content_by_lua return;\n    }\n--- request\n    GET /test\n--- response_body\nfailed to connect: missing the port number\n\n\n\n=== TEST 3: sanity\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        default_type 'text/plain';\n\n        server_tokens off;\n        location /foo {\n            content_by_lua 'ngx.say(\"foo\")';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /test {\n        rewrite_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                print(\"calling receive\")\n                local line, err = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err)\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n\n        content_by_lua return;\n    }\n--- request\n    GET /test\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed\nclose: 1 nil\n"
  },
  {
    "path": "t/023-rewrite/uthread-exec.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: exec in user thread (entry still pending)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.exec(\"/foo\")\n            end\n\n            ngx.thread.spawn(f)\n            ngx.sleep(1)\n            ngx.say(\"hello\")\n        ';\n        content_by_lua return;\n    }\n\n    location /foo {\n        echo i am foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\ni am foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: exec in user thread (entry already quits)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exec(\"/foo\")\n            end\n\n            ngx.thread.spawn(f)\n        ';\n        content_by_lua return;\n    }\n\n    location /foo {\n        echo i am foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\ni am foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: exec in user thread (entry thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exec(\"/foo\")\n            end\n\n            ngx.thread.spawn(f)\n            ngx.sleep(1)\n        ';\n        content_by_lua return;\n    }\n\n    location = /foo {\n        echo hello foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 1000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 1\nfree request\n\n--- response_body\nhello foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: exec in a user thread (another user thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exec(\"/foo\")\n            end\n\n            local function g()\n                ngx.sleep(1)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.thread.spawn(g)\n        ';\n        content_by_lua return;\n    }\n\n    location = /foo {\n        echo hello foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\ncreate 3 in 1\nspawn user thread 3 in 1\nadd timer 1000\nterminate 1: ok\ndelete thread 1\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 3\nfree request\n\n--- response_body\nhello foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: exec in user thread (entry thread is still pending on ngx.location.capture), without pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exec(\"/foo\")\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.location.capture(\"/sleep\")\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n\n    location = /foo {\n        echo hello world;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nend\n--- error_log\nattempt to abort with pending subrequests\n"
  },
  {
    "path": "t/023-rewrite/uthread-exit.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4 + 1);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: exit in user thread (entry thread is still pending to run)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            ngx.sleep(1)\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nM(timer-add) {\n    if ($arg2 == 1000) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nbefore\nhello in thread\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: exit in user thread (entry thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            ngx.sleep(1)\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 1000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 3: exit in a user thread (another user thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.say(\"f\")\n                ngx.exit(0)\n            end\n\n            local function g()\n                ngx.sleep(1)\n                ngx.say(\"g\")\n            end\n\n            ngx.thread.spawn(f)\n            ngx.thread.spawn(g)\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\ncreate 3 in 1\nspawn user thread 3 in 1\nadd timer 1000\nterminate 1: ok\ndelete thread 1\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 3\nfree request\n\n--- response_body\nend\nf\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: exit in user thread (entry already quits)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.say(\"exiting the user thread\")\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- wait: 0.1\n--- response_body\nbefore\nafter\nexiting the user thread\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: exit in user thread (entry thread is still pending on the DNS resolver for ngx.socket.tcp)\n--- config\n    location /lua {\n        resolver 127.0.0.2:12345;\n        resolver_timeout 12s;\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.001)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_resolve_start) {\n    println(\"resolver started\")\n}\n\nF(ngx_http_lua_socket_resolve_handler) {\n    println(\"resolver done\")\n}\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nF(ngx_resolve_name) {\n    printf(\"resolving %s\\n\", user_string_n($ctx->name->data, $ctx->name->len))\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 1) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 1) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 12000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 1) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_tcp_resolve_cleanup) {\n    println(\"lua tcp resolve cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 1\nresolver started\nresolving agentzh.org\nadd timer 12000\nexpire timer 1\nterminate 2: ok\ndelete thread 2\nlua tcp resolve cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: exit in user thread (entry thread is still pending on the DNS resolver for ngx.socket.udp)\n--- config\n    location /lua {\n        resolver 127.0.0.2:12345;\n        #resolver 127.0.0.1;\n        resolver_timeout 12s;\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.001)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.udp()\n            local ok, err = sock:setpeername(\"agentzh.org\", 80)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_resolve_start) {\n    println(\"resolver started\")\n}\n\nF(ngx_http_lua_socket_resolve_handler) {\n    println(\"resolver done\")\n}\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nF(ngx_resolve_name) {\n    printf(\"resolving %s\\n\", user_string_n($ctx->name->data, $ctx->name->len))\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 1) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 1) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 12000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 1) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_udp_resolve_cleanup) {\n    println(\"lua udp resolve cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 1\nresolver started\nresolving agentzh.org\nadd timer 12000\nexpire timer 1\nterminate 2: ok\ndelete thread 2\nlua udp resolve cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: exit in user thread (entry thread is still pending on tcpsock:connect)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.tcp()\n            sock:settimeout(12000)\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 12000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua tcp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: exit in user thread (entry thread is still pending on tcpsock:receive)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, ok = sock:send(\"blpop not_exists 2\\\\r\\\\n\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            sock:settimeout(12000)\n\n            local data, err = sock:receive()\n            if not data then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua tcp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: exit in user thread (entry thread is still pending on tcpsock:receiveuntil's iterator)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, ok = sock:send(\"blpop not_exists 2\\\\r\\\\n\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local it, err = sock:receiveuntil(\"\\\\r\\\\n\")\n            if not it then\n                ngx.say(\"failed to receive until: \", err)\n                return\n            end\n\n            sock:settimeout(12000)\n\n            local data, err = it()\n            if not data then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua tcp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: exit in user thread (entry thread is still pending on udpsock:receive)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.udp()\n\n            local ok, err = sock:setpeername(\"8.8.8.8\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            sock:settimeout(12000)\n\n            local data, err = sock:receive()\n            if not data then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_udp_socket_cleanup) {\n    println(\"lua udp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua udp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: exit in user thread (entry thread is still pending on reqsock:receive)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.req.socket()\n\n            sock:settimeout(12000)\n\n            local data, err = sock:receive(1024)\n            if not data then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nPOST /lua\n\n--- more_headers\nContent-Length: 1024\n\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua tcp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 12: exit in user thread (entry thread is still pending on ngx.req.read_body)\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n\n            ngx.req.read_body()\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nPOST /lua\n--- more_headers\nContent-Length: 1024\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_req_body_cleanup) {\n    println(\"lua req body cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua req body cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 13: exit in user thread (entry thread is still pending on ngx.location.capture), with pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nbefore\nhello in thread\nafter\nend\n--- error_log\nattempt to abort with pending subrequests\n\n\n\n=== TEST 14: exit in user thread (entry thread is still pending on ngx.location.capture), without pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.location.capture(\"/sleep\")\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq %s\\n\", ngx_http_req_uri($r))\n}\n\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\npost subreq /sleep\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nend\n--- error_log\nattempt to abort with pending subrequests\n\n\n\n=== TEST 15: exit in user thread (entry thread is still pending on ngx.location.capture_multi), without pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.location.capture_multi{\n                {\"/echo\"},\n                {\"/sleep\"}\n            }\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /echo {\n        echo hello;\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq %s\\n\", ngx_http_req_uri($r))\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\npost subreq /echo\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\npost subreq /sleep\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nend\n--- error_log\nattempt to abort with pending subrequests\n"
  },
  {
    "path": "t/023-rewrite/uthread-redirect.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: ngx.redirect() in user thread (entry thread is still pending on ngx.location.capture_multi), without pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.redirect(301)\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.location.capture_multi{\n                {\"/echo\"},\n                {\"/sleep\"}\n            }\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /echo {\n        echo hello;\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq %s\\n\", ngx_http_req_uri($r))\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\npost subreq /echo\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\npost subreq /sleep\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- response_body\nend\n--- error_log\nattempt to abort with pending subrequests\n\n\n\n=== TEST 2: redirect in user thread (entry thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.redirect(301)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.sleep(1)\n            ngx.say(\"end\")\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 1000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 1\nfree request\n\n--- response_body_like: 302 Found\n--- error_code: 302\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/023-rewrite/uthread-spawn.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4 + 1);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple user thread without I/O\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval\n<<'_EOC_' . $::StapScript;\n\nF(ngx_http_lua_send_chain_link) {\n    printf(\"send link %p\\n\", $in)\n}\n\nF(ngx_http_core_content_phase) {\n    println(\"core content phase\")\n}\n\n_EOC_\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: two simple user threads without I/O\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"in thread 1\")\n            end\n\n            local function g()\n                ngx.say(\"in thread 2\")\n            end\n\n            ngx.say(\"before 1\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after 1\")\n\n            ngx.say(\"before 2\")\n            ngx.thread.spawn(g)\n            ngx.say(\"after 2\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 3: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 3\ndelete thread 1\n\n--- response_body\nbefore 1\nin thread 1\nafter 1\nbefore 2\nin thread 2\nafter 2\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: simple user thread with sleep\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"before sleep\")\n                ngx.sleep(0.1)\n                ngx.say(\"after sleep\")\n            end\n\n            ngx.say(\"before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread create\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore thread create\nbefore sleep\nafter thread create\nafter sleep\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: two simple user threads with sleep\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"1: before sleep\")\n                ngx.sleep(0.2)\n                ngx.say(\"1: after sleep\")\n            end\n\n            local function g()\n                ngx.say(\"2: before sleep\")\n                ngx.sleep(0.1)\n                ngx.say(\"2: after sleep\")\n            end\n\n            ngx.say(\"1: before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"1: after thread create\")\n\n            ngx.say(\"2: before thread create\")\n            ngx.thread.spawn(g)\n            ngx.say(\"2: after thread create\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 3: ok\ndelete thread 3\nterminate 2: ok\ndelete thread 2\n\n--- wait: 0.1\n--- response_body\n1: before thread create\n1: before sleep\n1: after thread create\n2: before thread create\n2: before sleep\n2: after thread create\n2: after sleep\n1: after sleep\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: error in user thread\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.blah()\n            end\n\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: fail\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nafter\n--- error_log eval\nqr/lua user thread aborted: runtime error: rewrite_by_lua\\(nginx\\.conf:\\d+\\):3: attempt to call field 'blah' \\(a nil value\\)/\n\n\n\n=== TEST 6: simple user threads doing a single subrequest (entry quits early)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"before capture\")\n                local res = ngx.location.capture(\"/proxy\")\n                ngx.say(\"after capture: \", res.body)\n            end\n\n            ngx.say(\"before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread create\")\n        ';\n    }\n\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n\n    location /foo {\n        echo_sleep 0.1;\n        echo -n hello world;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore thread create\nbefore capture\nafter thread create\nafter capture: hello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: simple user threads doing a single subrequest (entry also does a subrequest and quits early)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"before capture\")\n                local res = ngx.location.capture(\"/proxy?foo\")\n                ngx.say(\"after capture: \", res.body)\n            end\n\n            ngx.say(\"before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread create\")\n            local res = ngx.location.capture(\"/proxy?bar\")\n            ngx.say(\"capture: \", res.body)\n        ';\n    }\n\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/$args;\n    }\n\n    location /foo {\n        echo_sleep 0.1;\n        echo -n hello foo;\n    }\n\n    location /bar {\n        echo -n hello bar;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore thread create\nbefore capture\nafter thread create\ncapture: hello bar\nafter capture: hello foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: simple user threads doing a single subrequest (entry also does a subrequest and quits late)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"before capture\")\n                local res = ngx.location.capture(\"/proxy?foo\")\n                ngx.say(\"after capture: \", res.body)\n            end\n\n            ngx.say(\"before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread create\")\n            local res = ngx.location.capture(\"/proxy?bar\")\n            ngx.say(\"capture: \", res.body)\n        ';\n    }\n\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/$args;\n    }\n\n    location /foo {\n        echo_sleep 0.1;\n        echo -n hello foo;\n    }\n\n    location /bar {\n        echo_sleep 0.2;\n        echo -n hello bar;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nbefore thread create\nbefore capture\nafter thread create\nafter capture: hello foo\ncapture: hello bar\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: two simple user threads doing single subrequests (entry also does a subrequest and quits between)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"f: before capture\")\n                local res = ngx.location.capture(\"/proxy?foo\")\n                ngx.say(\"f: after capture: \", res.body)\n            end\n\n            local function g()\n                ngx.say(\"g: before capture\")\n                local res = ngx.location.capture(\"/proxy?bah\")\n                ngx.say(\"g: after capture: \", res.body)\n            end\n\n            ngx.say(\"before thread 1 create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread 1 create\")\n\n            ngx.say(\"before thread 2 create\")\n            ngx.thread.spawn(g)\n            ngx.say(\"after thread 2 create\")\n\n            local res = ngx.location.capture(\"/proxy?bar\")\n            ngx.say(\"capture: \", res.body)\n        ';\n    }\n\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/$args;\n    }\n\n    location /foo {\n        echo_sleep 0.1;\n        echo -n hello foo;\n    }\n\n    location /bar {\n        echo_sleep 0.2;\n        echo -n hello bar;\n    }\n\n    location /bah {\n        echo_sleep 0.3;\n        echo -n hello bah;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nbefore thread 1 create\nf: before capture\nafter thread 1 create\nbefore thread 2 create\ng: before capture\nafter thread 2 create\nf: after capture: hello foo\ncapture: hello bar\ng: after capture: hello bah\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: nested user threads\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local g\n            local function f()\n                ngx.say(\"before g\")\n                ngx.thread.spawn(g)\n                ngx.say(\"after g\")\n            end\n\n            function g()\n                ngx.say(\"hello in g()\")\n            end\n\n            ngx.say(\"before f\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after f\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 3: ok\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 3\ndelete thread 2\n\n--- response_body\nbefore f\nbefore g\nhello in g()\nafter f\nafter g\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: nested user threads (with I/O)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local g\n            local function f()\n                ngx.say(\"before g\")\n                ngx.thread.spawn(g)\n                ngx.say(\"after g\")\n            end\n\n            function g()\n                ngx.sleep(0.1)\n                ngx.say(\"hello in g()\")\n            end\n\n            ngx.say(\"before f\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after f\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nbefore f\nbefore g\nafter f\nafter g\nhello in g()\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: coroutine status of a running user thread\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local co\n            local function f()\n                co = coroutine.running()\n                ngx.sleep(0.1)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.say(\"status: \", coroutine.status(co))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nstatus: running\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: coroutine status of a dead user thread\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local co\n            local function f()\n                co = coroutine.running()\n            end\n\n            ngx.thread.spawn(f)\n            ngx.say(\"status: \", coroutine.status(co))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nstatus: zombie\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: coroutine status of a \"normal\" user thread\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local co\n            local g\n            local function f()\n                co = coroutine.running()\n                local co2 = coroutine.create(g)\n                coroutine.resume(co2)\n            end\n\n            function g()\n                ngx.sleep(0.1)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.say(\"status: \", coroutine.status(co))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 2\nterminate 1: ok\ndelete thread 1\nterminate 3: ok\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nstatus: normal\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: creating user threads in a user coroutine\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local g\n            local function f()\n                ngx.say(\"before g\")\n                ngx.thread.spawn(g)\n                ngx.say(\"after g\")\n            end\n\n            function g()\n                ngx.say(\"hello in g()\")\n            end\n\n            ngx.say(\"before f\")\n            local co = coroutine.create(f)\n            coroutine.resume(co)\n            ngx.say(\"after f\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 3: ok\nterminate 2: ok\ndelete thread 3\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nbefore f\nbefore g\nhello in g()\nafter g\nafter f\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: manual time slicing between a user thread and the entry thread\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local yield = coroutine.yield\n\n            local function f()\n                local self = coroutine.running()\n                ngx.say(\"f 1\")\n                yield(self)\n                ngx.say(\"f 2\")\n                yield(self)\n                ngx.say(\"f 3\")\n            end\n\n            local self = coroutine.running()\n            ngx.say(\"0\")\n            yield(self)\n            ngx.say(\"1\")\n            ngx.thread.spawn(f)\n            ngx.say(\"2\")\n            yield(self)\n            ngx.say(\"3\")\n            yield(self)\n            ngx.say(\"4\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\n0\n1\nf 1\n2\nf 2\n3\nf 3\n4\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: manual time slicing between two user threads\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local yield = coroutine.yield\n\n            local function f()\n                local self = coroutine.running()\n                ngx.say(\"f 1\")\n                yield(self)\n                ngx.say(\"f 2\")\n                yield(self)\n                ngx.say(\"f 3\")\n            end\n\n            local function g()\n                local self = coroutine.running()\n                ngx.say(\"g 1\")\n                yield(self)\n                ngx.say(\"g 2\")\n                yield(self)\n                ngx.say(\"g 3\")\n            end\n\n            ngx.thread.spawn(f)\n            ngx.thread.spawn(g)\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nf 1\ng 1\nf 2\ndone\ng 2\nf 3\ng 3\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: entry thread and a user thread flushing at the same time\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                coroutine.yield(coroutine.running)\n                ngx.flush(true)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            ngx.flush(true)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: two user threads flushing at the same time\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.say(\"hello from f\")\n                ngx.flush(true)\n            end\n\n            local function g()\n                ngx.say(\"hello from g\")\n                ngx.flush(true)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.thread.spawn(g)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like\n^(?:create 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3|create 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 3: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 3\ndelete thread 1)$\n\n--- response_body\nhello from f\nhello from g\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: user threads + ngx.socket.tcp\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n                local bytes, err = sock:send(\"flush_all\\\\r\\\\n\")\n                if not bytes then\n                    ngx.say(\"failed to send query: \", err)\n                    return\n                end\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore\nafter\nreceived: OK\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: user threads + ngx.socket.udp\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                local sock = ngx.socket.udp()\n                local ok, err = sock:setpeername(\"127.0.0.1\", 12345)\n                local bytes, err = sock:send(\"blah\")\n                if not bytes then\n                    ngx.say(\"failed to send query: \", err)\n                    return\n                end\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like chop\n^(?:create 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n|create 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1)$\n\n--- udp_listen: 12345\n--- udp_query: blah\n--- udp_reply: hello udp\n--- response_body_like chop\n^(?:before\nafter\nreceived: hello udp\n|before\nreceived: hello udp\nafter)$\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: simple user thread with ngx.req.read_body()\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.req.read_body()\n                local body = ngx.req.get_body_data()\n                ngx.say(\"body: \", body)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n        content_by_lua return;\n    }\n--- request\nPOST /lua\nhello world\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like chop\n^(?:create 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1|create 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2)$\n\n--- response_body_like chop\n^(?:before\nbody: hello world\nafter|before\nafter\nbody: hello world)$\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: simple user thread with ngx.req.socket()\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                local sock = ngx.req.socket()\n                local body, err = sock:receive(11)\n                if not body then\n                    ngx.say(\"failed to read body: \", err)\n                    return\n                end\n\n                ngx.say(\"body: \", body)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nPOST /lua\nhello world\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like chop\n^(?:create 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1|create 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2)$\n\n--- response_body_like chop\n^(?:before\nbody: hello world\nafter|before\nafter\nbody: hello world)$\n\n--- no_error_log\n[error]\n--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 24: multiple user threads + subrequests returning 404 immediately\n--- config\n    location /t {\n        rewrite_by_lua '\n            local capture = ngx.location.capture\n            local insert = table.insert\n\n            local function f(i)\n                local res = capture(\"/proxy/\" .. i)\n                ngx.say(\"status: \", res.status)\n            end\n\n            local threads = {}\n            for i = 1, 2 do\n                local co = ngx.thread.spawn(f, i)\n                insert(threads, co)\n            end\n\n            ngx.say(\"ok\")\n        ';\n    }\n\n    location ~ ^/proxy/(\\d+) {\n        return 404;\n    }\n--- request\n    GET /t\n--- stap2 eval: $::StapScript\n--- stap eval\n\"$::GCScript\"\n.\n'\nF(ngx_http_finalize_request) {\n    printf(\"finalize request %s: rc:%d c:%d a:%d\\n\", ngx_http_req_uri($r), $rc, $r->main->count, $r == $r->main);\n    #if ($rc == -1) {\n        #print_ubacktrace()\n    #}\n}\n\nM(http-subrequest-done) {\n    printf(\"subrequest %s done\\n\", ngx_http_req_uri($r))\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: %s rc=%d, status=%d a=%d\\n\", ngx_http_req_uri($r), $rc,\n         $r->headers_out->status, $r == $r->main)\n    #print_ubacktrace()\n}\n'\n--- stap_out_like chop\n^create 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nfinalize request /proxy/1: rc:404 c:3 a:0\npost subreq: /proxy/1 rc=404, status=0 a=0\nsubrequest /proxy/1 done\nterminate 2: ok\ndelete thread 2\nfinalize request /proxy/2: rc:404 c:2 a:0\npost subreq: /proxy/2 rc=404, status=0 a=0\nsubrequest /proxy/2 done\nterminate 3: ok\ndelete thread 3\nfinalize request /t: rc:200 c:1 a:1\n(?:finalize request /t: rc:0 c:1 a:1)?$\n\n--- response_body\nok\nstatus: 404\nstatus: 404\n--- no_error_log\n[error]\n--- timeout: 3\n\n\n\n=== TEST 25: multiple user threads + subrequests returning 404 remotely (no wait)\n--- config\n    location /t {\n        rewrite_by_lua '\n            local capture = ngx.location.capture\n            local insert = table.insert\n\n            local function f(i)\n                local res = capture(\"/proxy/\" .. i)\n                ngx.say(\"status: \", res.status)\n            end\n\n            local threads = {}\n            for i = 1, 5 do\n                local co = ngx.thread.spawn(f, i)\n                insert(threads, co)\n            end\n\n            ngx.say(\"ok\")\n        ';\n    }\n\n    location ~ ^/proxy/(\\d+) {\n        proxy_pass http://127.0.0.1:$server_port/d/$1;\n    }\n\n    location /d {\n        return 404;\n        #echo $uri;\n    }\n--- request\n    GET /t\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like chop\n^create 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\ncreate 4 in 1\nspawn user thread 4 in 1\ncreate 5 in 1\nspawn user thread 5 in 1\ncreate 6 in 1\nspawn user thread 6 in 1\nterminate 1: ok\ndelete thread 1\n(?:terminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\nterminate 4: ok\ndelete thread 4\nterminate 5: ok\ndelete thread 5\nterminate 6: ok\ndelete thread 6|terminate 6: ok\ndelete thread 6\nterminate 5: ok\ndelete thread 5\nterminate 4: ok\ndelete thread 4\nterminate 3: ok\ndelete thread 3\nterminate 2: ok\ndelete thread 2)$\n\n--- response_body\nok\nstatus: 404\nstatus: 404\nstatus: 404\nstatus: 404\nstatus: 404\n--- no_error_log\n[error]\n--- timeout: 6\n\n\n\n=== TEST 26: multiple user threads + subrequests returning 201 immediately\n--- config\n    location /t {\n        rewrite_by_lua '\n            local capture = ngx.location.capture\n            local insert = table.insert\n\n            local function f(i)\n                local res = capture(\"/proxy/\" .. i)\n                ngx.say(\"status: \", res.status)\n            end\n\n            local threads = {}\n            for i = 1, 2 do\n                local co = ngx.thread.spawn(f, i)\n                insert(threads, co)\n            end\n\n            ngx.say(\"ok\")\n        ';\n    }\n\n    location ~ ^/proxy/(\\d+) {\n        content_by_lua 'ngx.exit(201)';\n    }\n--- request\n    GET /t\n--- stap2 eval: $::StapScript\n--- stap3\nF(ngx_http_send_header) {\n    printf(\"send header main_req=%d, r=%p\\n\", $r == $r->main, $r)\n    print_ubacktrace()\n    printf(\"========================================\")\n}\n--- stap_out3\n--- stap eval\n\"$::GCScript\"\n.\n'\nF(ngx_http_finalize_request) {\n    printf(\"finalize request %s: rc:%d c:%d a:%d\\n\", ngx_http_req_uri($r), $rc, $r->main->count, $r == $r->main);\n    #if ($rc == -1) {\n        #print_ubacktrace()\n    #}\n}\n\nM(http-subrequest-done) {\n    printf(\"subrequest %s done\\n\", ngx_http_req_uri($r))\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: %s rc=%d, status=%d a=%d\\n\", ngx_http_req_uri($r), $rc,\n         $r->headers_out->status, $r == $r->main)\n    #print_ubacktrace()\n}\n'\n--- stap_out_like chop\n^create 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 4: ok\ndelete thread 4\nfinalize request /proxy/1: rc:0 c:3 a:0\npost subreq: /proxy/1 rc=0, status=201 a=0\nsubrequest /proxy/1 done\nterminate 2: ok\ndelete thread 2\nterminate 5: ok\ndelete thread 5\nfinalize request /proxy/2: rc:0 c:2 a:0\npost subreq: /proxy/2 rc=0, status=201 a=0\nsubrequest /proxy/2 done\nterminate 3: ok\ndelete thread 3\nfinalize request /t: rc:200 c:1 a:1\n(?:finalize request /t: rc:0 c:1 a:1)?$\n\n--- response_body\nok\nstatus: 201\nstatus: 201\n--- no_error_log\n[error]\n--- timeout: 3\n\n\n\n=== TEST 27: multiple user threads + subrequests returning 204 immediately\n--- config\n    location /t {\n        rewrite_by_lua '\n            local capture = ngx.location.capture\n            local insert = table.insert\n\n            local function f(i)\n                local res = capture(\"/proxy/\" .. i)\n                ngx.say(\"status: \", res.status)\n            end\n\n            local threads = {}\n            for i = 1, 2 do\n                local co = ngx.thread.spawn(f, i)\n                insert(threads, co)\n            end\n\n            ngx.say(\"ok\")\n        ';\n    }\n\n    location ~ ^/proxy/(\\d+) {\n        content_by_lua 'ngx.exit(204)';\n    }\n--- request\n    GET /t\n--- stap2 eval: $::StapScript\n--- stap eval\n\"$::GCScript\"\n.\n'\nF(ngx_http_finalize_request) {\n    printf(\"finalize request %s: rc:%d c:%d a:%d\\n\", ngx_http_req_uri($r), $rc, $r->main->count, $r == $r->main);\n    #if ($rc == -1) {\n        #print_ubacktrace()\n    #}\n}\n\nM(http-subrequest-done) {\n    printf(\"subrequest %s done\\n\", ngx_http_req_uri($r))\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: %s rc=%d, status=%d a=%d\\n\", ngx_http_req_uri($r), $rc,\n         $r->headers_out->status, $r == $r->main)\n    #print_ubacktrace()\n}\n'\n\n--- stap_out_like chop\n^create 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 4: ok\ndelete thread 4\nfinalize request /proxy/1: rc:204 c:3 a:0\npost subreq: /proxy/1 rc=204, status=204 a=0\nsubrequest /proxy/1 done\nterminate 2: ok\ndelete thread 2\nterminate 5: ok\ndelete thread 5\nfinalize request /proxy/2: rc:204 c:2 a:0\npost subreq: /proxy/2 rc=204, status=204 a=0\nsubrequest /proxy/2 done\nterminate 3: ok\ndelete thread 3\nfinalize request /t: rc:200 c:1 a:1\n(?:finalize request /t: rc:0 c:1 a:1)?$\n\n--- response_body\nok\nstatus: 204\nstatus: 204\n--- no_error_log\n[error]\n--- timeout: 3\n"
  },
  {
    "path": "t/024-access/auth.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n#no_nginx_manager();\n\n#repeat_each(1);\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 1);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: basic test passing\n--- config\n    location /lua {\n        lua_need_request_body on;\n        client_max_body_size 100k;\n        client_body_buffer_size 100k;\n\n        access_by_lua '\n            -- check the client IP addr is in our black list\n            if ngx.var.remote_addr == \"132.5.72.3\" then\n                ngx.exit(ngx.HTTP_FORBIDDEN)\n            end\n\n            -- check if the request body contains bad words\n            if ngx.var.request_body and string.match(ngx.var.request_body, \"fuck\") then\n                return ngx.redirect(\"/terms_of_use.html\")\n            end\n\n            -- tests passed\n        ';\n\n        echo Logged in;\n    }\n--- request\nGET /lua\n--- response_body\nLogged in\n\n\n\n=== TEST 2: bad words in request body\n--- config\n    location /lua {\n        lua_need_request_body on;\n        client_max_body_size 100k;\n        client_body_buffer_size 100k;\n\n        access_by_lua '\n            -- check the client IP addr is in our black list\n            if ngx.var.remote_addr == \"132.5.72.3\" then\n                ngx.exit(ngx.HTTP_FORBIDDEN)\n            end\n\n            -- check if the request body contains bad words\n            if ngx.var.request_body and string.match(ngx.var.request_body, \"fuck\") then\n                return ngx.redirect(\"/terms_of_use.html\")\n            end\n\n            -- tests passed\n        ';\n\n        echo Logged in;\n    }\n--- request\nPOST /lua\nHe fucks himself!\n--- response_body_like: 302 Found\n--- response_headers_like\nLocation: /terms_of_use\\.html\n--- error_code: 302\n\n\n\n=== TEST 3: client IP\n--- config\n    location /lua {\n        lua_need_request_body on;\n        client_max_body_size 100k;\n        client_body_buffer_size 100k;\n\n        access_by_lua '\n            -- check the client IP addr is in our black list\n            if ngx.var.remote_addr == \"127.0.0.1\" then\n                ngx.exit(ngx.HTTP_FORBIDDEN)\n            end\n\n            -- check if the request body contains bad words\n            if ngx.var.request_body and string.match(ngx.var.request_body, \"fuck\") then\n                return ngx.redirect(\"/terms_of_use.html\")\n            end\n\n            -- tests passed\n        ';\n\n        echo Logged in;\n    }\n--- request\nGET /lua\n--- response_body_like: 403 Forbidden\n--- error_code: 403\n"
  },
  {
    "path": "t/024-access/client-abort.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_USE_HTTP3}) {\n        $SkipReason = \"http3 does not support ngx.req.socket and lua_check_client_abort\";\n    } elsif ($ENV{TEST_NGINX_USE_HTTP2}) {\n        $SkipReason = \"http2 does not support ngx.req.socket and lua_check_client_abort\";\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\nuse t::StapThread;\n\nour $GCScript = <<_EOC_;\n$t::StapThread::GCScript\n\nF(ngx_http_lua_check_broken_connection) {\n    println(\"lua check broken conn\")\n}\n\nF(ngx_http_lua_request_cleanup) {\n    println(\"lua req cleanup\")\n}\n_EOC_\n\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 - 1);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            ngx.sleep(1)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 0.1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 2: sleep + stop (log handler still gets called)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            ngx.sleep(1)\n        ';\n        log_by_lua '\n            ngx.log(ngx.NOTICE, \"here in log by lua\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- wait: 0.1\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\nhere in log by lua\n\n\n\n=== TEST 3: sleep + ignore\n--- config\n    location /t {\n        lua_check_client_abort off;\n        access_by_lua '\n            ngx.sleep(1)\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nlua req cleanup\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: subrequest + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            ngx.location.capture(\"/sub\")\n            error(\"bad things happen\")\n        ';\n    }\n\n    location /sub {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 5: subrequest + ignore\n--- config\n    location /t {\n        lua_check_client_abort off;\n        access_by_lua '\n            ngx.location.capture(\"/sub\")\n            error(\"bad things happen\")\n        ';\n    }\n\n    location /sub {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: fail\nlua req cleanup\ndelete thread 1\n\n--- wait: 1.1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- error_log\nbad things happen\n\n\n\n=== TEST 6: subrequest + stop (proxy, ignore client abort)\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            ngx.location.capture(\"/sub\")\n            error(\"bad things happen\")\n        ';\n    }\n\n    location = /sub {\n        proxy_ignore_client_abort on;\n        proxy_pass http://127.0.0.2:12345/;\n    }\n\n    location = /sleep {\n        lua_check_client_abort on;\n        access_by_lua '\n            ngx.sleep(1)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 7: subrequest + stop (proxy, check client abort)\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            ngx.location.capture(\"/sub\")\n            error(\"bad things happen\")\n        ';\n    }\n\n    location = /sub {\n        proxy_ignore_client_abort off;\n        proxy_pass http://127.0.0.2:12345/;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 8: need body on + sleep + stop (log handler still gets called)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        lua_need_request_body on;\n        access_by_lua '\n            ngx.sleep(1)\n        ';\n        log_by_lua '\n            ngx.log(ngx.NOTICE, \"here in log by lua\")\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\nhere in log by lua\n\n\n\n=== TEST 9: ngx.req.read_body + sleep + stop (log handler still gets called)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            ngx.req.read_body()\n            ngx.sleep(1)\n        ';\n        log_by_lua '\n            ngx.log(ngx.NOTICE, \"here in log by lua\")\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\nhere in log by lua\n\n\n\n=== TEST 10: ngx.req.socket + receive() + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local sock = ngx.req.socket()\n            sock:receive()\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 11: ngx.req.socket + receive(N) + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local sock = ngx.req.socket()\n            sock:receive(5)\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 0.1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 12: ngx.req.socket + receive(n) + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local sock = ngx.req.socket()\n            sock:receive(2)\n            ngx.sleep(1)\n        ';\n        content_by_lua return;\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like\n^(?:lua check broken conn\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nlua req cleanup|lua check broken conn\nlua req cleanup\ndelete thread 1)$\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: ngx.req.socket + m * receive(n) + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local sock = ngx.req.socket()\n            sock:receive(2)\n            sock:receive(2)\n            sock:receive(1)\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 14: ngx.req.socket + receiveuntil + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local sock = ngx.req.socket()\n            local it = sock:receiveuntil(\"\\\\n\")\n            it()\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 15: ngx.req.socket + receiveuntil + it(n) + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local sock = ngx.req.socket()\n            local it = sock:receiveuntil(\"\\\\n\")\n            it(2)\n            it(3)\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 16: cosocket + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            ngx.req.discard_body()\n\n            local sock, err = ngx.socket.tcp()\n            if not sock then\n                ngx.log(ngx.ERR, \"failed to get socket: \", err)\n                return\n            end\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"blpop nonexist 2\\\\r\\\\n\")\n            if not bytes then\n                ngx.log(ngx.ERR, \"failed to send query: \", err)\n                return\n            end\n\n            -- ngx.log(ngx.ERR, \"about to receive\")\n\n            local res, err = sock:receive()\n            if not res then\n                ngx.log(ngx.ERR, \"failed to receive query: \", err)\n                return\n            end\n\n            ngx.log(ngx.ERR, \"res: \", res)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 17: ngx.req.socket + receive n < content-length + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local sock = ngx.req.socket()\n            local res, err = sock:receive(\"*a\")\n            if not res then\n                ngx.log(ngx.NOTICE, \"failed to receive: \", err)\n                return\n            end\n            error(\"bad\")\n        ';\n        content_by_lua return;\n    }\n--- raw_request eval\n\"POST /t HTTP/1.0\\r\nHost: localhost\\r\nConnection: close\\r\nContent-Length: 100\\r\n\\r\nhello\"\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nfailed to receive: client aborted\n\n\n\n=== TEST 18: ngx.req.socket + receive n == content-length + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local sock = ngx.req.socket()\n            local res, err = sock:receive(\"*a\")\n            if not res then\n                ngx.log(ngx.NOTICE, \"failed to receive: \", err)\n                return\n            end\n            ngx.sleep(1)\n            error(\"bad\")\n        ';\n\n        content_by_lua return;\n    }\n--- raw_request eval\n\"POST /t HTTP/1.0\\r\nHost: localhost\\r\nConnection: close\\r\nContent-Length: 5\\r\n\\r\nhello\"\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 19: ngx.req.socket + receive n == content-length + ignore\n--- config\n    location /t {\n        access_by_lua '\n            local sock = ngx.req.socket()\n            local res, err = sock:receive(\"*a\")\n            if not res then\n                ngx.log(ngx.NOTICE, \"failed to receive: \", err)\n                return\n            end\n            ngx.say(\"done\")\n        ';\n        content_by_lua return;\n    }\n--- raw_request eval\n\"POST /t HTTP/1.0\\r\nHost: localhost\\r\nConnection: close\\r\nContent-Length: 5\\r\n\\r\nhello\"\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nlua req cleanup\n\n--- shutdown: 1\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 20: ngx.req.read_body + sleep + stop (log handler still gets called)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            ngx.req.read_body()\n        ';\n        content_by_lua return;\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nlua req cleanup\n\n--- shutdown: 1\n--- wait: 0.1\n--- ignore_response\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: exec to lua + ignore\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            ngx.exec(\"/t2\")\n        ';\n    }\n\n    location = /t2 {\n        lua_check_client_abort off;\n        content_by_lua '\n            ngx.sleep(1)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\nlua req cleanup\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nlua req cleanup\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 22: exec to proxy + ignore\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            ngx.exec(\"/t2\")\n        ';\n    }\n\n    location = /t2 {\n        proxy_ignore_client_abort on;\n        proxy_pass http://127.0.0.1:$server_port/sleep;\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 23: exec (named location) to proxy + ignore\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            ngx.exec(\"@t2\")\n        ';\n    }\n\n    location @t2 {\n        proxy_ignore_client_abort on;\n        proxy_pass http://127.0.0.1:$server_port/sleep;\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n"
  },
  {
    "path": "t/024-access/exec.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 6);\n\n#no_diff();\n#no_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /read {\n        access_by_lua '\n            ngx.exec(\"/hi\");\n            ngx.say(\"Hi\");\n        ';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body\nHello\n\n\n\n=== TEST 2: empty uri arg\n--- config\n    location /read {\n        access_by_lua '\n            ngx.exec(\"\");\n            ngx.say(\"Hi\");\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 3: no arg\n--- config\n    location /read {\n        access_by_lua '\n            ngx.exec();\n            ngx.say(\"Hi\");\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 4: too many args\n--- config\n    location /read {\n        access_by_lua '\n            ngx.exec(1, 2, 3, 4);\n            ngx.say(\"Hi\");\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 5: null uri\n--- config\n    location /read {\n        access_by_lua '\n            ngx.exec(nil)\n            ngx.say(\"Hi\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 6: user args\n--- config\n    location /read {\n        access_by_lua '\n            ngx.exec(\"/hi\", \"Yichun Zhang\")\n            ngx.say(\"Hi\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo Hello $query_string;\n    }\n--- request\nGET /read\n--- response_body\nHello Yichun Zhang\n\n\n\n=== TEST 7: args in uri\n--- config\n    location /read {\n        access_by_lua '\n            ngx.exec(\"/hi?agentzh\")\n            ngx.say(\"Hi\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo Hello $query_string;\n    }\n--- request\nGET /read\n--- response_body\nHello agentzh\n\n\n\n=== TEST 8: args in uri and user args\n--- config\n    location /read {\n        access_by_lua '\n            ngx.exec(\"/hi?a=Yichun\", \"b=Zhang\")\n            ngx.say(\"Hi\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /hi {\n        echo Hello $query_string;\n    }\n--- request\nGET /read\n--- response_body\nHello a=Yichun&b=Zhang\n\n\n\n=== TEST 9: args in uri and user args\n--- config\n    location /read {\n        access_by_lua '\n            ngx.exec(\"@hi?a=Yichun\", \"b=Zhang\")\n            ngx.say(\"Hi\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location @hi {\n        echo Hello $query_string;\n    }\n--- request\nGET /read\n--- response_body\nHello \n\n\n\n=== TEST 10: exec after location capture\n--- config\n    location /test {\n        access_by_lua_file 'html/test.lua';\n        echo world;\n    }\n\n    location /a {\n        echo \"hello\";\n    }\n\n    location /b {\n        echo \"hello\";\n    }\n\n--- user_files\n>>> test.lua\nngx.location.capture('/a')\n\nngx.exec('/b')\n--- request\n    GET /test\n--- response_body\nhello\n\n\n\n=== TEST 11: exec after (named) location capture\n--- config\n    location /test {\n        access_by_lua_file 'html/test.lua';\n    }\n\n    location /a {\n        echo \"hello\";\n    }\n\n    location @b {\n        echo \"hello\";\n    }\n\n--- user_files\n>>> test.lua\nngx.location.capture('/a')\n\nngx.exec('@b')\n--- request\n    GET /test\n--- response_body\nhello\n\n\n\n=== TEST 12: github issue #40: 2 Subrequest calls when using access_by_lua, ngx.exec and echo_location\n--- config\n    location = /hi {\n        echo hello;\n    }\n    location /sub {\n        proxy_pass http://127.0.0.1:$server_port/hi;\n    }\n    location /p{\n        #content_by_lua '\n            #local res = ngx.location.capture(\"/sub\")\n            #ngx.print(res.body)\n        #';\n        echo_location /sub;\n    }\n    location /lua {\n        access_by_lua '\n            ngx.exec(\"/p\")\n        ';\n    }\n--- request\n    GET /lua\n--- response_body\nhello\n--- timeout: 3\n\n\n\n=== TEST 13: github issue #40: 2 Subrequest calls when using access_by_lua, ngx.exec and echo_location (named location)\n--- config\n    location = /hi {\n        echo hello;\n    }\n    location /sub {\n        proxy_pass http://127.0.0.1:$server_port/hi;\n    }\n    location @p {\n        #content_by_lua '\n            #local res = ngx.location.capture(\"/sub\")\n            #ngx.print(res.body)\n        #';\n        echo_location /sub;\n    }\n    location /lua {\n        access_by_lua '\n            ngx.exec(\"@p\")\n        ';\n    }\n--- request\n    GET /lua\n--- response_body\nhello\n\n\n\n=== TEST 14: github issue #40: 2 Subrequest calls when using access_by_lua, ngx.exec and echo_location (post subrequest)\n--- config\n    location = /hi {\n        echo hello;\n    }\n    location /sub {\n        proxy_pass http://127.0.0.1:$server_port/hi;\n    }\n    location /p{\n        #content_by_lua '\n            #local res = ngx.location.capture(\"/sub\")\n            #ngx.print(res.body)\n        #';\n        echo_location /sub;\n    }\n    location /blah {\n        echo blah;\n    }\n    location /lua {\n        access_by_lua '\n            ngx.location.capture(\"/blah\")\n            ngx.exec(\"/p\")\n        ';\n    }\n--- request\n    GET /lua\n--- response_body\nhello\n\n\n\n=== TEST 15: access_by_lua + ngx.exec + subrequest capture\n--- config\n    location /main {\n        access_by_lua '\n            local res = ngx.location.capture(\"/test_loc\");\n            ngx.print(\"hello, \", res.body)\n        ';\n        content_by_lua return;\n    }\n    location /test_loc {\n        rewrite_by_lua '\n            ngx.exec(\"@proxy\")\n        ';\n    }\n    location @proxy {\n        #echo proxy;\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n    location /foo {\n        echo bah;\n    }\n--- request\n    GET /main\n--- response_body\nhello, bah\n\n\n\n=== TEST 16: github issue #905: unsafe uri\n--- config\n    location /read {\n        access_by_lua_block {\n            ngx.exec(\"/hi/../\");\n        }\n    }\n    location /hi {\n        echo \"Hello\";\n    }\n--- request\nGET /read\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\n[\n'unsafe URI \"/hi/../\" was detected',\nqr/runtime error: access_by_lua\\(nginx.conf:\\d+\\):2: unsafe uri/,\n]\n\n\n\n=== TEST 17: pipelined requests\n--- config\n    location /t {\n        access_by_lua_block {\n            ngx.exec(\"@foo\")\n        }\n    }\n\n    location @foo {\n        return 200;\n    }\n--- pipelined_requests eval\n[\"GET /t\", \"GET /t\"]\n--- error_code eval\n[200, 200]\n--- response_body eval\n[\"\", \"\"]\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/024-access/exit.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#repeat_each(20000);\nrepeat_each(2);\n\n#master_on();\n#workers(1);\n#log_level('debug');\n#log_level('warn');\n#worker_connections(1024);\n\nplan tests => repeat_each() * (blocks() * 2 + 2);\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n\nour $LuaCpath = $ENV{LUA_CPATH} ||\n    '/usr/local/openresty-debug/lualib/?.so;/usr/local/openresty/lualib/?.so;;';\n\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: throw 403\n--- config\n    location /lua {\n        access_by_lua \"ngx.exit(403);ngx.say('hi')\";\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- error_code: 403\n--- response_body_like: 403 Forbidden\n\n\n\n=== TEST 2: throw 404\n--- config\n    location /lua {\n        access_by_lua \"ngx.exit(404);ngx.say('hi');\";\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- error_code: 404\n--- response_body_like: 404 Not Found\n\n\n\n=== TEST 3: throw 404 after sending the header and partial body\n--- config\n    location /lua {\n        access_by_lua \"ngx.say('hi');ngx.exit(404);ngx.say(', you')\";\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nhi\n--- no_error_log\n[alert]\n--- error_log\nattempt to set status 404 via ngx.exit after sending out the response status 200\n\n\n\n=== TEST 4: working with ngx_auth_request (succeeded)\n--- config\n    location /auth {\n        access_by_lua \"\n            if ngx.var.user == 'agentzh' then\n                ngx.eof();\n            else\n                ngx.exit(403)\n            end\";\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n    location /api {\n        set $user $arg_user;\n        auth_request /auth;\n\n        echo \"Logged in\";\n    }\n--- request\nGET /api?user=agentzh\n--- error_code: 200\n--- response_body\nLogged in\n\n\n\n=== TEST 5: working with ngx_auth_request (failed)\n--- config\n    location /api {\n        set $user $arg_user;\n        access_by_lua \"\n            if ngx.var.user == 'agentzh' then\n                ngx.eof();\n            else\n                ngx.exit(403)\n            end\";\n\n        echo \"Logged in\";\n    }\n--- request\nGET /api?user=agentz\n--- error_code: 403\n--- response_body_like: 403 Forbidden\n\n\n\n=== TEST 6: working with ngx_auth_request (simplest form, w/o ngx_memc)\n--- no_http2\n--- http_config eval\n\"\n    lua_package_cpath '$::LuaCpath';\n    upstream backend {\n        drizzle_server 127.0.0.1:\\$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n\"\n--- config\n    location /memc {\n        internal;\n\n        set $memc_key $arg_key;\n        set $memc_exptime $arg_exptime;\n\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /conv-uid-mysql {\n        internal;\n\n        set $key \"conv-uid-$arg_uid\";\n\n        #srcache_fetch GET /memc key=$key;\n        #srcache_store PUT /memc key=$key;\n\n        default_type 'application/json';\n\n        drizzle_query \"select new_uid as uid from conv_uid where old_uid=$arg_uid\";\n        drizzle_pass backend;\n\n        rds_json on;\n    }\n\n    location /api {\n        set $uid $arg_uid;\n        access_by_lua_file 'html/foo.lua';\n\n        echo \"Logged in $uid\";\n    }\n--- user_files\n>>> foo.lua\nlocal cjson = require('cjson');\nlocal old_uid = ngx.var.uid\nprint('about to run sr')\nlocal res = ngx.location.capture('/conv-uid-mysql?uid=' .. old_uid)\nprint('just have run sr' .. res.body)\nif (res.status ~= ngx.HTTP_OK) then\n    -- ngx.exit(res.status)\nend\nres = cjson.decode(res.body)\nif (not res or not res[1] or not res[1].uid or\n        not string.match(res[1].uid, '^%d+$')) then\n    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\nend\nngx.var.uid = res[1].uid;\n-- print('done')\n--- request\nGET /api?uid=32\n--- response_body\nLogged in 56\n--- timeout: 3\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 7: working with ngx_auth_request (simplest form)\n--- no_http2\n--- http_config eval\n\"\n    lua_package_cpath '$::LuaCpath';\n    upstream backend {\n        drizzle_server 127.0.0.1:\\$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n\"\n--- config\n    location /memc {\n        internal;\n\n        set $memc_key $arg_key;\n        set $memc_exptime $arg_exptime;\n\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /conv-uid-mysql {\n        internal;\n\n        set $key \"conv-uid-$arg_uid\";\n\n        #srcache_fetch GET /memc key=$key;\n        #srcache_store PUT /memc key=$key;\n\n        default_type 'application/json';\n\n        drizzle_query \"select new_uid as uid from conv_uid where old_uid=$arg_uid\";\n        drizzle_pass backend;\n\n        rds_json on;\n    }\n\n    location /api {\n        set $uid $arg_uid;\n        access_by_lua_file html/foo.lua;\n\n        echo \"Logged in $uid\";\n    }\n--- user_files\n>>> foo.lua\nlocal cjson = require('cjson');\nlocal old_uid = ngx.var.uid\n-- print('about to run sr')\nlocal res = ngx.location.capture('/conv-uid-mysql?uid=' .. old_uid)\n-- print('just have run sr' .. res.body)\nif (res.status ~= ngx.HTTP_OK) then\n    ngx.exit(res.status)\nend\nres = cjson.decode(res.body)\nif (not res or not res[1] or not res[1].uid or\n        not string.match(res[1].uid, '^%d+$')) then\n    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\nend\nngx.var.uid = res[1].uid;\n-- print('done')\n--- request\nGET /api?uid=32\n--- response_body\nLogged in 56\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 8: working with ngx_auth_request\n--- no_http2\n--- http_config eval\n\"\n    lua_package_cpath '$::LuaCpath';\n    upstream backend {\n        drizzle_server 127.0.0.1:\\$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n\n    upstream memc_a {\n        server 127.0.0.1:\\$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    upstream memc_b {\n        server 127.0.0.1:\\$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    upstream_list memc_cluster memc_a memc_b;\n\"\n--- config\n    location /memc {\n        internal;\n\n        set $memc_key $arg_key;\n        set $memc_exptime $arg_exptime;\n\n        set_hashed_upstream $backend memc_cluster $arg_key;\n        memc_pass $backend;\n    }\n\n    location /conv-uid-mysql {\n        internal;\n\n        set $key \"conv-uid-$arg_uid\";\n\n        #srcache_fetch GET /memc key=$key;\n        #srcache_store PUT /memc key=$key;\n\n        default_type 'application/json';\n\n        drizzle_query \"select new_uid as uid from conv_uid where old_uid=$arg_uid\";\n        drizzle_pass backend;\n\n        rds_json on;\n    }\n\n    location /api {\n        set $uid $arg_uid;\n        access_by_lua_file html/foo.lua;\n\n        echo \"Logged in $uid\";\n    }\n--- user_files\n>>> foo.lua\nlocal cjson = require('cjson');\nlocal old_uid = ngx.var.uid\n-- print('about to run sr')\nlocal res = ngx.location.capture('/conv-uid-mysql?uid=' .. old_uid)\n-- print('just have run sr' .. res.body)\nif (res.status ~= ngx.HTTP_OK) then\n    ngx.exit(res.status)\nend\nres = cjson.decode(res.body)\nif (not res or not res[1] or not res[1].uid or\n        not string.match(res[1].uid, '^%d+$')) then\n    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\nend\nngx.var.uid = res[1].uid;\n-- print('done')\n--- request\nGET /api?uid=32\n--- response_body\nLogged in 56\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 9: working with ngx_auth_request\n--- http_config\n    upstream backend {\n        drizzle_server 127.0.0.1:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n\n    upstream memc_a {\n        server 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n        keepalive 300;\n    }\n\n    #upstream_list memc_cluster memc_a memc_b;\n\n--- config\n    location /memc {\n        internal;\n\n        set $memc_key $arg_key;\n        set $memc_exptime $arg_exptime;\n\n        #set_hashed_upstream $backend memc_cluster $arg_key;\n        memc_pass memc_a;\n    }\n\n    location /conv-mysql {\n        internal;\n\n        set $key \"conv-uri-$query_string\";\n\n        #srcache_fetch GET /memc key=$key;\n        #srcache_store PUT /memc key=$key;\n\n        default_type 'application/json';\n\n        set_quote_sql_str $seo_uri $query_string;\n        drizzle_query \"select url from my_url_map where seo_url=$seo_uri\";\n        drizzle_pass backend;\n\n        rds_json on;\n    }\n\n    location /conv-uid {\n        internal;\n        access_by_lua_file 'html/foo.lua';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n\n    location /baz {\n        set $my_uri $uri;\n        auth_request /conv-uid;\n\n        echo_exec /jump $my_uri;\n    }\n\n    location /jump {\n        internal;\n        rewrite ^ $query_string? redirect;\n    }\n--- user_files\n>>> foo.lua\nlocal cjson = require('cjson');\nlocal seo_uri = ngx.var.my_uri\n-- print('about to run sr')\nlocal res = ngx.location.capture('/conv-mysql?' .. seo_uri)\nif (res.status ~= ngx.HTTP_OK) then\n    ngx.exit(res.status)\nend\nres = cjson.decode(res.body)\nif (not res or not res[1] or not res[1].url) then\n    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\nend\nngx.var.my_uri = res[1].url;\n-- print('done')\n--- request\nGET /baz\n--- response_body_like: 302\n--- error_code: 302\n--- response_headers\nLocation: http://localhost:$ServerPort/foo/bar\n--- SKIP\n\n\n\n=== TEST 10: throw 0\n--- config\n    location /lua {\n        access_by_lua \"ngx.say('Hi'); ngx.eof(); ngx.exit(0);ngx.say('world')\";\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- error_code: 200\n--- response_body\nHi\n\n\n\n=== TEST 11: throw ngx.OK does *not* skip other later phase handlers\n--- config\n    location /lua {\n        access_by_lua \"ngx.exit(ngx.OK)\";\n        set $foo hello;\n        echo $foo;\n    }\n--- request\nGET /lua\n--- response_body\nhello\n\n\n\n=== TEST 12: throw ngx.HTTP_OK *does* skip other later phase handlers (by inlined code)\n--- config\n    location /lua {\n        access_by_lua \"ngx.exit(ngx.HTTP_OK)\";\n        set $foo hello;\n        echo $foo;\n    }\n--- request\nGET /lua\n--- response_body\n\n\n\n=== TEST 13: throw ngx.HTTP_OK *does* skip other rewrite phase handlers (by inlined code + partial output)\n--- config\n    location /lua {\n        rewrite_by_lua \"ngx.say('hiya') ngx.exit(ngx.HTTP_OK)\";\n        set $foo hello;\n        echo $foo;\n    }\n--- request\nGET /lua\n--- response_body\nhiya\n\n\n\n=== TEST 14: throw ngx.HTTP_OK *does* skip other later phase handlers (by file)\n--- config\n    location /lua {\n        access_by_lua_file html/foo.lua;\n        set $foo hello;\n        echo $foo;\n    }\n--- user_files\n>>> foo.lua\nngx.exit(ngx.HTTP_OK)\n--- request\nGET /lua\n--- response_body\n\n\n\n=== TEST 15: throw ngx.HTTP_OK *does* skip other rewrite phase handlers (by file + partial output)\n--- config\n    location /lua {\n        rewrite_by_lua_file html/foo.lua;\n        set $foo hello;\n        echo $foo;\n    }\n--- user_files\n>>> foo.lua\nngx.say(\"morning\")\nngx.exit(ngx.HTTP_OK)\n--- request\nGET /lua\n--- response_body\nmorning\n\n\n\n=== TEST 16: error page with custom body\n--- config\n    error_page 410 @err;\n    location @err {\n        echo blah blah;\n    }\n    location /foo {\n        access_by_lua '\n            ngx.status = ngx.HTTP_GONE\n            ngx.say(\"This is our own content\")\n            -- to cause quit the whole request rather than the current phase handler\n            ngx.exit(ngx.HTTP_OK)\n        ';\n        echo Hello;\n    }\n--- request\n    GET /foo\n--- response_body\nThis is our own content\n--- error_code: 410\n\n\n\n=== TEST 17: exit(404) after I/O\n--- config\n    error_page 400 /400.html;\n    error_page 404 /404.html;\n    location /foo {\n        access_by_lua '\n            ngx.location.capture(\"/sleep\")\n            ngx.exit(ngx.HTTP_NOT_FOUND)\n        ';\n        echo Hello;\n    }\n\n    location /sleep {\n        echo_sleep 0.002;\n    }\n--- user_files\n>>> 400.html\nBad request, dear...\n>>> 404.html\nNot found, dear...\n--- request\n    GET /bah\n--- response_body\nNot found, dear...\n--- error_code: 404\n"
  },
  {
    "path": "t/024-access/mixed.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 2 + 4);\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: access I/O with content I/O\n--- config\n    location /flush {\n        set $memc_cmd flush_all;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /memc {\n        set $memc_key $echo_request_uri;\n        set $memc_exptime 600;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /lua {\n        access_by_lua '\n            ngx.location.capture(\"/flush\");\n\n            local res = ngx.location.capture(\"/memc\");\n            print(\"access GET: \", res.status);\n\n            res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n            print(\"access PUT: \", res.status);\n\n            res = ngx.location.capture(\"/memc\");\n            print(\"access cached: \", res.body);\n        ';\n\n        content_by_lua '\n            ngx.location.capture(\"/flush\");\n\n            local res = ngx.location.capture(\"/memc\");\n            ngx.say(\"content GET: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n            ngx.say(\"content PUT: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\");\n            ngx.say(\"content cached: \" .. res.body);\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ncontent GET: 404\ncontent PUT: 201\ncontent cached: hello\n--- grep_error_log eval: qr/access .+?(?= while )/\n--- grep_error_log_out\naccess GET: 404\naccess PUT: 201\naccess cached: hello\n\n--- log_level: info\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 2: share data via nginx variables\n--- config\n    location /foo {\n        set $foo '';\n        access_by_lua '\n            ngx.var.foo = 32\n        ';\n\n        content_by_lua '\n            ngx.say(tonumber(ngx.var.foo) * 2)\n        ';\n    }\n--- request\n    GET /foo\n--- response_body\n64\n\n\n\n=== TEST 3: share the request body (need request body explicitly off)\n--- config\n    location /echo_body {\n        lua_need_request_body off;\n        set $res '';\n        access_by_lua '\n            ngx.var.res = ngx.var.request_body or \"nil\"\n        ';\n        content_by_lua '\n            ngx.say(ngx.var.res or \"nil\")\n            ngx.say(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body\nnil\nnil\n\n\n\n=== TEST 4: share the request body (need request body off by default)\n--- config\n    location /echo_body {\n        #lua_need_request_body off;\n        set $res '';\n        access_by_lua '\n            ngx.var.res = ngx.var.request_body or \"nil\"\n        ';\n        content_by_lua '\n            ngx.say(ngx.var.res or \"nil\")\n            ngx.say(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body\nnil\nnil\n\n\n\n=== TEST 5: share the request body (need request body on)\n--- config\n    location /echo_body {\n        lua_need_request_body on;\n        set $res '';\n        access_by_lua '\n            ngx.var.res = ngx.var.request_body or \"nil\"\n        ';\n        content_by_lua '\n            ngx.say(ngx.var.res or \"nil\")\n            ngx.say(ngx.var.request_body or \"nil\")\n        ';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"hello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\n\"\n\n\n\n=== TEST 6: rewrite I/O with access I/O with content I/O\n--- config\n    location /flush {\n        set $memc_cmd flush_all;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /memc {\n        set $memc_key $echo_request_uri;\n        set $memc_exptime 600;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /lua {\n        rewrite_by_lua '\n            ngx.location.capture(\"/flush\");\n\n            local res = ngx.location.capture(\"/memc\");\n            print(\"rewrite GET: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n            print(\"rewrite PUT: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\");\n            print(\"rewrite cached: \" .. res.body);\n        ';\n\n        access_by_lua '\n            ngx.location.capture(\"/flush\");\n\n            local res = ngx.location.capture(\"/memc\");\n            print(\"access GET: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n            print(\"access PUT: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\");\n            print(\"access cached: \" .. res.body);\n        ';\n\n        content_by_lua '\n            ngx.location.capture(\"/flush\");\n\n            local res = ngx.location.capture(\"/memc\");\n            ngx.say(\"content GET: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n            ngx.say(\"content PUT: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\");\n            ngx.say(\"content cached: \" .. res.body);\n\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ncontent GET: 404\ncontent PUT: 201\ncontent cached: hello\n\n--- grep_error_log eval: qr/(?:rewrite|access) .+?(?= while )/\n--- grep_error_log_out\nrewrite GET: 404\nrewrite PUT: 201\nrewrite cached: hello\naccess GET: 404\naccess PUT: 201\naccess cached: hello\n\n--- log_level: info\n\n\n\n=== TEST 7: I/O in access shortcuts content automatically\n--- config\n    location = /t {\n        access_by_lua_block {\n            ngx.print(\"\")\n        }\n\n        echo ok;\n    }\n--- request\n    GET /t\n--- response_body\n"
  },
  {
    "path": "t/024-access/multi-capture.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(10);\n\nplan tests => blocks() * repeat_each() * 2;\n\n#$ENV{LUA_PATH} = $ENV{HOME} . '/work/JSON4Lua-0.9.30/json/?.lua';\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /foo {\n        access_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n        ';\n        content_by_lua return;\n    }\n    location /a {\n        echo -n a;\n    }\n    location /b {\n        echo -n b;\n    }\n--- request\n    GET /foo\n--- response_body\nres1.status = 200\nres1.body = a\nres2.status = 200\nres2.body = b\n\n\n\n=== TEST 2: 4 concurrent requests\n--- config\n    location /foo {\n        access_by_lua '\n            local res1, res2, res3, res4 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n                { \"/c\" },\n                { \"/d\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n\n            ngx.say(\"res3.status = \" .. res3.status)\n            ngx.say(\"res3.body = \" .. res3.body)\n\n            ngx.say(\"res4.status = \" .. res4.status)\n            ngx.say(\"res4.body = \" .. res4.body)\n        ';\n        content_by_lua return;\n    }\n    location ~ '^/([a-d])$' {\n        echo -n $1;\n    }\n--- request\n    GET /foo\n--- response_body\nres1.status = 200\nres1.body = a\nres2.status = 200\nres2.body = b\nres3.status = 200\nres3.body = c\nres4.status = 200\nres4.body = d\n\n\n\n=== TEST 3: capture multi in series\n--- config\n    location /foo {\n        access_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n\n            res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            ngx.say(\"2 res1.status = \" .. res1.status)\n            ngx.say(\"2 res1.body = \" .. res1.body)\n            ngx.say(\"2 res2.status = \" .. res2.status)\n            ngx.say(\"2 res2.body = \" .. res2.body)\n\n        ';\n        content_by_lua return;\n    }\n    location /a {\n        echo -n a;\n    }\n    location /b {\n        echo -n b;\n    }\n--- request\n    GET /foo\n--- response_body\nres1.status = 200\nres1.body = a\nres2.status = 200\nres2.body = b\n2 res1.status = 200\n2 res1.body = a\n2 res2.status = 200\n2 res2.body = b\n\n\n\n=== TEST 4: capture multi in subrequest\n--- config\n    location /foo {\n        rewrite_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n\n            local n = ngx.var.arg_n\n\n            ngx.say(n .. \" res1.status = \" .. res1.status)\n            ngx.say(n .. \" res1.body = \" .. res1.body)\n            ngx.say(n .. \" res2.status = \" .. res2.status)\n            ngx.say(n .. \" res2.body = \" .. res2.body)\n        ';\n        content_by_lua return;\n    }\n\n    location /main {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo?n=1\")\n            ngx.say(\"top res.status = \" .. res.status)\n            ngx.say(\"top res.body = [\" .. res.body .. \"]\")\n        ';\n        content_by_lua return;\n    }\n\n    location /a {\n        echo -n a;\n    }\n\n    location /b {\n        echo -n b;\n    }\n--- request\n    GET /main\n--- response_body\ntop res.status = 200\ntop res.body = [1 res1.status = 200\n1 res1.body = a\n1 res2.status = 200\n1 res2.body = b\n]\n\n\n\n=== TEST 5: capture multi in parallel\n--- config\n    location ~ '^/(foo|bar)$' {\n        set $tag $1;\n        rewrite_by_lua '\n            local res1, res2\n            if ngx.var.tag == \"foo\" then\n                res1, res2 = ngx.location.capture_multi{\n                    { \"/a\" },\n                    { \"/b\" },\n                }\n            else\n                res1, res2 = ngx.location.capture_multi{\n                    { \"/c\" },\n                    { \"/d\" },\n                }\n            end\n\n            local n = ngx.var.arg_n\n\n            ngx.say(n .. \" res1.status = \" .. res1.status)\n            ngx.say(n .. \" res1.body = \" .. res1.body)\n            ngx.say(n .. \" res2.status = \" .. res2.status)\n            ngx.say(n .. \" res2.body = \" .. res2.body)\n        ';\n        content_by_lua return;\n    }\n\n    location /main {\n        access_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/foo?n=1\" },\n                { \"/bar?n=2\" },\n            }\n\n            ngx.say(\"top res1.status = \" .. res1.status)\n            ngx.say(\"top res1.body = [\" .. res1.body .. \"]\")\n            ngx.say(\"top res2.status = \" .. res2.status)\n            ngx.say(\"top res2.body = [\" .. res2.body .. \"]\")\n        ';\n        content_by_lua return;\n    }\n\n    location ~ '^/([abcd])$' {\n        echo -n $1;\n    }\n--- request\n    GET /main\n--- response_body\ntop res1.status = 200\ntop res1.body = [1 res1.status = 200\n1 res1.body = a\n1 res2.status = 200\n1 res2.body = b\n]\ntop res2.status = 200\ntop res2.body = [2 res1.status = 200\n2 res1.body = c\n2 res2.status = 200\n2 res2.body = d\n]\n\n\n\n=== TEST 6: memc sanity\n--- config\n    location /foo {\n        access_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n        ';\n        content_by_lua return;\n    }\n    location ~ '^/[ab]$' {\n        set $memc_key $uri;\n        set $memc_value hello;\n        set $memc_cmd set;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n--- request\n    GET /foo\n--- response_body eval\n\"res1.status = 201\nres1.body = STORED\\r\n\nres2.status = 201\nres2.body = STORED\\r\n\n\"\n\n\n\n=== TEST 7: memc muti + multi\n--- config\n    location /main {\n        access_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/foo?n=1\" },\n                { \"/bar?n=2\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = [\" .. res1.body .. \"]\")\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = [\" .. res2.body .. \"]\")\n        ';\n        content_by_lua return;\n    }\n    location ~ '^/(foo|bar)$' {\n        set $tag $1;\n        rewrite_by_lua '\n            local res1, res2\n            if ngx.var.tag == \"foo\" then\n                res1, res2 = ngx.location.capture_multi{\n                    { \"/a\" },\n                    { \"/b\" },\n                }\n            else\n                res1, res2 = ngx.location.capture_multi{\n                    { \"/c\" },\n                    { \"/d\" },\n                }\n            end\n            print(\"args: \" .. ngx.var.args)\n            local n = ngx.var.arg_n\n            ngx.say(n .. \" res1.status = \" .. res1.status)\n            ngx.say(n .. \" res1.body = \" .. res1.body)\n            ngx.say(n .. \" res2.status = \" .. res2.status)\n            ngx.say(n .. \" res2.body = \" .. res2.body)\n        ';\n        content_by_lua return;\n    }\n    location ~ '^/[abcd]$' {\n        set $memc_key $uri;\n        set $memc_value hello;\n        set $memc_cmd set;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n--- request\n    GET /main\n--- response_body eval\n\"res1.status = 200\nres1.body = [1 res1.status = 201\n1 res1.body = STORED\\r\n\n1 res2.status = 201\n1 res2.body = STORED\\r\n\n]\nres2.status = 200\nres2.body = [2 res1.status = 201\n2 res1.body = STORED\\r\n\n2 res2.status = 201\n2 res2.body = STORED\\r\n\n]\n\"\n\n\n\n=== TEST 8: memc 4 concurrent requests\n--- config\n    location /foo {\n        access_by_lua '\n            local res1, res2, res3, res4 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n                { \"/c\" },\n                { \"/d\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n\n            ngx.say(\"res3.status = \" .. res3.status)\n            ngx.say(\"res3.body = \" .. res3.body)\n\n            ngx.say(\"res4.status = \" .. res4.status)\n            ngx.say(\"res4.body = \" .. res4.body)\n        ';\n        content_by_lua return;\n    }\n    location ~ '^/[a-d]$' {\n        set $memc_key $uri;\n        set $memc_value hello;\n        set $memc_cmd set;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n--- request\n    GET /foo\n--- response_body eval\n\"res1.status = 201\nres1.body = STORED\\r\n\nres2.status = 201\nres2.body = STORED\\r\n\nres3.status = 201\nres3.body = STORED\\r\n\nres4.status = 201\nres4.body = STORED\\r\n\n\"\n"
  },
  {
    "path": "t/024-access/on-abort.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = <<_EOC_;\n$t::StapThread::GCScript\n\nF(ngx_http_lua_check_broken_connection) {\n    println(\"lua check broken conn\")\n}\n\nF(ngx_http_lua_request_cleanup) {\n    println(\"lua req cleanup\")\n}\n_EOC_\n\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    plan(skip_all => \"HTTP3 does not support on_abort\");\n} elsif (defined $ENV{TEST_NGINX_USE_HTTP2}) {\n    plan(skip_all => \"HTTP2 does not support on_abort\");\n} else {\n    plan tests => repeat_each() * (blocks() * 4 + 15);\n}\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: ignore the client abort event in the user callback\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nterminate 3: ok\ndelete thread 3\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- wait: 0.7\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 2: abort in the user callback\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(444)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\nmain handler done\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 3: ngx.exit(499) with pending subrequest\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(499)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n        ';\n        content_by_lua return;\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 4: ngx.exit(408) with pending subrequest\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(408)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n        ';\n        content_by_lua return;\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- wait: 0.1\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 5: ngx.exit(-1) with pending subrequest\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(-1)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 6: ngx.exit(0) with pending subrequest\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(0)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n            ngx.log(ngx.ERR, \"main handler done\")\n        ';\n        content_by_lua return;\n    }\n\n    location = /sleep {\n        echo_sleep 0.7;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: fail\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nterminate 3: ok\ndelete thread 3\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- wait: 0.6\n--- ignore_response\n--- error_log eval\n[\n'client prematurely closed connection',\n'on abort called',\nqr/lua user thread aborted: runtime error: access_by_lua\\(nginx\\.conf:\\d+\\):4: attempt to abort with pending subrequests/,\n'main handler done',\n]\n\n\n\n=== TEST 7: accessing cosocket in callback\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n                if not ok then\n                    ngx.log(ngx.ERR, \"failed to connect to redis: \", err)\n                    ngx.exit(499)\n                end\n                local bytes, err = sock:send(\"flushall\\\\r\\\\n\")\n                if not bytes then\n                    ngx.log(ngx.ERR, \"failed to send query: \", err)\n                    ngx.exit(499)\n                end\n\n                local res, err = sock:receive()\n                if not res then\n                    ngx.log(ngx.ERR, \"failed to receive: \", err)\n                    ngx.exit(499)\n                end\n                ngx.log(ngx.NOTICE, \"callback done: \", res)\n                ngx.exit(499)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like chop\n^create 2 in 1\nlua check broken conn\n(?:lua check broken conn\n)?terminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n$\n--- timeout: 0.2\n--- abort\n--- wait: 0.2\n--- ignore_response\n--- no_error_log\n[error]\nmain handler done\n--- error_log\nclient prematurely closed connection\non abort called\ncallback done: +OK\n\n\n\n=== TEST 8: ignore the client abort event in the user callback (no check)\n--- config\n    location /t {\n        lua_check_client_abort off;\n        access_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                ngx.say(\"cannot set on_abort: \", err)\n                return\n            end\n\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- response_body\ncannot set on_abort: lua_check_client_abort is off\n--- no_error_log\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 9: register on_abort callback but no client abortion\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nlua req cleanup\ndelete thread 2\n\n--- response_body\ndone\n--- no_error_log\n[error]\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 10: ignore the client abort event in the user callback (uthread)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.7)\n                ngx.log(ngx.NOTICE, \"main handler done\")\n            end)\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nlua check broken conn\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\nterminate 4: ok\ndelete thread 4\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- wait: 0.7\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 11: abort in the user callback (uthread)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(444)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.7)\n                ngx.log(ngx.NOTICE, \"main handler done\")\n            end)\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 3\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\nmain handler done\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 12: register on_abort callback but no client abortion (uthread)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.1)\n                ngx.say(\"done\")\n            end)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 3: ok\ndelete thread 3\nlua req cleanup\ndelete thread 2\n\n--- response_body\ndone\n--- no_error_log\n[error]\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 13: register on_abort callback multiple times\n--- config\n    location /t {\n        lua_check_client_abort on;\n        access_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                ngx.say(\"1: cannot set on_abort: \" .. err)\n                return\n            end\n\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                ngx.say(\"2: cannot set on_abort: \" .. err)\n                return\n            end\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.1)\n                ngx.say(\"done\")\n            end)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nlua req cleanup\ndelete thread 2\n\n--- response_body\n2: cannot set on_abort: duplicate call\n\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/024-access/redirect.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => blocks() * repeat_each() * 3;\n\n#no_diff();\n#no_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: default 302\n--- config\n    location /read {\n        access_by_lua '\n            ngx.redirect(\"http://www.taobao.com/foo\");\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://www.taobao.com/foo\n--- response_body_like: 302 Found\n--- error_code: 302\n\n\n\n=== TEST 2: explicit 302\n--- config\n    location /read {\n        access_by_lua '\n            ngx.redirect(\"http://www.taobao.com/foo\", ngx.HTTP_MOVED_TEMPORARILY);\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://www.taobao.com/foo\n--- response_body_like: 302 Found\n--- error_code: 302\n\n\n\n=== TEST 3: explicit 301\n--- config\n    location /read {\n        access_by_lua '\n            ngx.redirect(\"http://www.taobao.com/foo\", ngx.HTTP_MOVED_PERMANENTLY);\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n    }\n--- request\nGET /read\n--- response_headers\nLocation: http://www.taobao.com/foo\n--- response_body_like: 301 Moved Permanently\n--- error_code: 301\n\n\n\n=== TEST 4: bad rc\n--- config\n    location /read {\n        access_by_lua '\n            ngx.redirect(\"http://www.taobao.com/foo\", 404);\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n    }\n--- request\nGET /read\n--- response_headers\n!Location\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 5: no args\n--- config\n    location /read {\n        access_by_lua '\n            ngx.redirect()\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n    }\n--- request\nGET /read\n--- response_headers\n!Location\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 6: relative uri\n--- config\n    location /read {\n        access_by_lua '\n            ngx.redirect(\"/foo\")\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n    }\n--- request\nGET /read\n--- raw_response_headers_like eval\nmy $headers;\n\nif (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    $headers = \"location: /foo\\r\\n\"\n} else {\n    $headers = \"Location: /foo\\r\\n\"\n}\n\n$headers;\n--- response_body_like: 302 Found\n--- error_code: 302\n"
  },
  {
    "path": "t/024-access/req-body.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 19);\n\n#no_diff();\n#no_long_string();\n#master_on();\n#workers(2);\nrun_tests();\n\n__DATA__\n\n=== TEST 1: read buffered body\n--- config\n    location = /test {\n        access_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_body)\n        ';\n        content_by_lua return;\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nhello, world\n\n\n\n=== TEST 2: read buffered body (timed out)\n--- config\n    client_body_timeout 1ms;\n    location = /test {\n        access_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_body)\n        ';\n        content_by_lua return;\n    }\n--- raw_request eval\n\"POST /test HTTP/1.1\\r\nHost: localhost\\r\nContent-Length: 100\\r\nConnection: close\\r\n\\r\nhello, world\"\n--- response_body:\n--- error_code_like: ^(?:500)?$\n\n\n\n=== TEST 3: read buffered body and then subrequest\n--- config\n    location /foo {\n        echo -n foo;\n    }\n    location = /test {\n        access_by_lua '\n            ngx.req.read_body()\n            local res = ngx.location.capture(\"/foo\");\n            ngx.say(ngx.var.request_body)\n            ngx.say(\"sub: \", res.body)\n        ';\n        content_by_lua return;\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nhello, world\nsub: foo\n\n\n\n=== TEST 4: first subrequest and then read buffered body\n--- config\n    location /foo {\n        echo -n foo;\n    }\n    location = /test {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\");\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_body)\n            ngx.say(\"sub: \", res.body)\n        ';\n        content_by_lua return;\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nhello, world\nsub: foo\n\n\n\n=== TEST 5: failed to write 100 continue\n--- config\n    location = /test {\n        access_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_body)\n            ngx.exit(200)\n        ';\n    }\n--- request\nPOST /test\nhello, world\n--- more_headers\nExpect: 100-Continue\n--- ignore_response\n--- no_error_log\n[alert]\n[error]\nhttp finalize request: 500, \"/test?\" a:1, c:0\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 6: not discard body (exit 200)\n--- config\n    location = /foo {\n        access_by_lua '\n            -- ngx.req.discard_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n            ngx.exit(200)\n        ';\n    }\n    location = /bar {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /foo\nhello, world\",\n\"POST /bar\nhiya, world\"]\n--- response_body eval\n[\"body: nil\\n\",\n\"body: hiya, world\\n\",\n]\n--- error_code eval\n[200, 200]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 7: not discard body (exit 201)\n--- config\n    location = /foo {\n        access_by_lua '\n            -- ngx.req.discard_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n            ngx.exit(201)\n        ';\n    }\n    location = /bar {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /foo\nhello, world\",\n\"POST /bar\nhiya, world\"]\n--- response_body eval\n[\"body: nil\\n\",\n\"body: hiya, world\\n\",\n]\n--- error_code eval\n[200, 200]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 8: not discard body (exit 302)\n--- config\n    location = /foo {\n        access_by_lua '\n            -- ngx.req.discard_body()\n            -- ngx.say(\"body: \", ngx.var.request_body)\n            ngx.redirect(\"/blah\")\n        ';\n    }\n    location = /bar {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /foo\nhello, world\",\n\"POST /bar\nhiya, world\"]\n--- response_body eval\n[qr/302 Found/,\n\"body: hiya, world\\n\",\n]\n--- error_code eval\n[302, 200]\n--- no_error_log\n[error]\n[alert]\n"
  },
  {
    "path": "t/024-access/request_body.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('debug'); # to ensure any log-level can be outputted\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 2);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: test reading request body\n--- config\n    location /echo_body {\n        lua_need_request_body on;\n        access_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"hello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n\n\n\n=== TEST 2: test not reading request body\n--- config\n    location /echo_body {\n        lua_need_request_body off;\n        access_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"nil\"\n\n\n\n=== TEST 3: test default setting (not reading request body)\n--- config\n    location /echo_body {\n        access_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"nil\"\n\n\n\n=== TEST 4: test main conf\n--- http_config\n    lua_need_request_body on;\n--- config\n    location /echo_body {\n        access_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"hello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n\n\n\n=== TEST 5: test server conf\n--- config\n    lua_need_request_body on;\n\n    location /echo_body {\n        access_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"hello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n\n\n\n=== TEST 6: test override main conf\n--- http_config\n    lua_need_request_body on;\n--- config\n    location /echo_body {\n        lua_need_request_body off;\n        access_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"nil\"\n\n\n\n=== TEST 7: test override server conf\n--- config\n    lua_need_request_body on;\n\n    location /echo_body {\n        lua_need_request_body off;\n        access_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request eval\n\"POST /echo_body\nhello\\x00\\x01\\x02\nworld\\x03\\x04\\xff\"\n--- response_body eval\n\"nil\"\n\n\n\n=== TEST 8: Expect: 100-Continue\n--- config\n    location /echo_body {\n        lua_need_request_body on;\n        access_by_lua '\n            ngx.print(ngx.var.request_body or \"nil\")\n            ngx.exit(200)\n        ';\n    }\n--- request\nPOST /echo_body\nhello world\n--- more_headers\nExpect: 100-Continue\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\nhttp finalize request: 500, \"/echo_body?\" a:1, c:2\nhttp finalize request: 500, \"/echo_body?\" a:1, c:0\n--- log_level: debug\n"
  },
  {
    "path": "t/024-access/sanity.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#log_level('warn');\n#no_nginx_manager();\n#master_on();\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 11);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: basic print\n--- config\n    location /lua {\n        # NOTE: the newline escape sequence must be double-escaped, as nginx config\n        # parser will unescape first!\n        access_by_lua 'ngx.print(\"Hello, Lua!\\\\n\")';\n        content_by_lua return;\n        #content_by_lua 'ngx.say(\"Hi\")';\n    }\n--- request\nGET /lua\n--- response_body\nHello, Lua!\n\n\n\n=== TEST 2: basic say\n--- config\n    location /say {\n        # NOTE: the newline escape sequence must be double-escaped, as nginx config\n        # parser will unescape first!\n        access_by_lua '\n            ngx.say(\"Hello, Lua!\")\n            ngx.say(\"Yay! \", 123)';\n\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /say\n--- response_body\nHello, Lua!\nYay! 123\n\n\n\n=== TEST 3: no ngx.echo\n--- config\n    location /lua {\n        access_by_lua 'ngx.echo(\"Hello, Lua!\\\\n\")';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 4: variable\n--- config\n    location /lua {\n        # NOTE: the newline escape sequence must be double-escaped, as nginx config\n        # parser will unescape first!\n        access_by_lua 'local v = ngx.var[\"request_uri\"] ngx.print(\"request_uri: \", v, \"\\\\n\")';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua?a=1&b=2\n--- response_body\nrequest_uri: /lua?a=1&b=2\n\n\n\n=== TEST 5: variable (file)\n--- config\n    location /lua {\n        access_by_lua_file html/test.lua;\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- user_files\n>>> test.lua\nlocal v = ngx.var[\"request_uri\"]\nngx.print(\"request_uri: \", v, \"\\n\")\n--- request\nGET /lua?a=1&b=2\n--- response_body\nrequest_uri: /lua?a=1&b=2\n\n\n\n=== TEST 6: calc expression\n--- config\n    location /lua {\n        access_by_lua_file html/calc.lua;\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- user_files\n>>> calc.lua\nlocal function uri_unescape(uri)\n    local function convert(hex)\n        return string.char(tonumber(\"0x\"..hex))\n    end\n    local s = string.gsub(uri, \"%%([0-9a-fA-F][0-9a-fA-F])\", convert)\n    return s\nend\n\nlocal function eval_exp(str)\n    return loadstring(\"return \"..str)()\nend\n\nlocal exp_str = ngx.var[\"arg_exp\"]\n-- print(\"exp: '\", exp_str, \"'\\n\")\nlocal status, res\nstatus, res = pcall(uri_unescape, exp_str)\nif not status then\n    ngx.print(\"error: \", res, \"\\n\")\n    return\nend\nstatus, res = pcall(eval_exp, res)\nif status then\n    ngx.print(\"result: \", res, \"\\n\")\nelse\n    ngx.print(\"error: \", res, \"\\n\")\nend\n--- request\nGET /lua?exp=1%2B2*math.sin(3)%2Fmath.exp(4)-math.sqrt(2)\n--- response_body\nresult: -0.4090441561579\n\n\n\n=== TEST 7: read $arg_xxx\n--- config\n    location = /lua {\n        access_by_lua 'local who = ngx.var.arg_who\n            ngx.print(\"Hello, \", who, \"!\")';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua?who=agentzh\n--- response_body chomp\nHello, agentzh!\n\n\n\n=== TEST 8: capture location\n--- config\n    location /other {\n        echo \"hello, world\";\n    }\n\n    location /lua {\n        access_by_lua '\nlocal res = ngx.location.capture(\"/other\")\nngx.print(\"status=\", res.status, \" \")\nngx.print(\"body=\", res.body)\n';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nstatus=200 body=hello, world\n\n\n\n=== TEST 9: capture non-existed location\n--- config\n    location /lua {\n        access_by_lua 'local res = ngx.location.capture(\"/other\"); ngx.print(\"status=\", res.status)';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body: status=404\n\n\n\n=== TEST 10: invalid capture location (not as expected...)\n--- config\n    location /lua {\n        access_by_lua 'local res = ngx.location.capture(\"*(#*\"); ngx.say(\"res=\", res.status)';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nres=404\n\n\n\n=== TEST 11: nil is \"nil\"\n--- config\n    location /lua {\n        access_by_lua 'ngx.say(nil)';\n        content_by_lua return;\n    }\n--- request\nGET /lua\n--- response_body\nnil\n\n\n\n=== TEST 12: write boolean\n--- config\n    location /lua {\n        access_by_lua 'ngx.say(true, \" \", false)';\n        content_by_lua return;\n    }\n--- request\nGET /lua\n--- response_body\ntrue false\n\n\n\n=== TEST 13: bad argument type to ngx.location.capture\n--- config\n    location /lua {\n        access_by_lua 'ngx.location.capture(nil)';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 14: capture location (default 0);\n--- config\n location /recur {\n       access_by_lua '\n           local num = tonumber(ngx.var.arg_num) or 0;\n           ngx.print(\"num is: \", num, \"\\\\n\");\n\n           if (num > 0) then\n               local res = ngx.location.capture(\"/recur?num=\"..tostring(num - 1));\n               ngx.print(\"status=\", res.status, \" \");\n               ngx.print(\"body=\", res.body, \"\\\\n\");\n           else\n               ngx.print(\"end\\\\n\");\n           end\n           ';\n\n           content_by_lua 'ngx.exit(ngx.OK)';\n   }\n--- request\nGET /recur\n--- response_body\nnum is: 0\nend\n\n\n\n=== TEST 15: capture location\naccess phase not running in subrequests\n--- config\n location /recur {\n       access_by_lua '\n           local num = tonumber(ngx.var.arg_num) or 0;\n           ngx.print(\"num is: \", num, \"\\\\n\");\n\n           if (num > 0) then\n               local res = ngx.location.capture(\"/recur?num=\"..tostring(num - 1));\n               ngx.print(\"status=\", res.status, \" \");\n               ngx.print(\"body=\", res.body);\n           else\n               ngx.print(\"end\\\\n\");\n           end\n           ';\n\n           content_by_lua 'ngx.exit(ngx.OK)';\n   }\n--- request\nGET /recur?num=3\n--- response_body chomp\nnum is: 3\nstatus=200 body=\n\n\n\n=== TEST 16: setting nginx variables from within Lua\n--- config\n location /set {\n       set $a \"\";\n       access_by_lua 'ngx.var.a = 32; ngx.say(ngx.var.a)';\n       content_by_lua 'ngx.exit(ngx.OK)';\n       add_header Foo $a;\n   }\n--- request\nGET /set\n--- response_headers\nFoo: 32\n--- response_body\n32\n\n\n\n=== TEST 17: nginx quote sql string 1\n--- config\n location /set {\n       set $a 'hello\\n\\r\\'\"\\\\'; # this runs after access_by_lua\n       access_by_lua 'ngx.say(ngx.quote_sql_str(ngx.var.a))';\n       content_by_lua 'ngx.exit(ngx.OK)';\n   }\n--- request\nGET /set\n--- response_body\n'hello\\n\\r\\'\\\"\\\\'\n\n\n\n=== TEST 18: nginx quote sql string 2\n--- config\nlocation /set {\n    #set $a \"hello\\n\\r'\\\"\\\\\";\n    access_by_lua 'ngx.say(ngx.quote_sql_str(\"hello\\\\n\\\\r\\'\\\\\"\\\\\\\\\"))';\n    content_by_lua 'ngx.exit(ngx.OK)';\n}\n--- request\nGET /set\n--- response_body\n'hello\\n\\r\\'\\\"\\\\'\n\n\n\n=== TEST 19: use dollar\n--- config\nlocation /set {\n    access_by_lua '\n        local s = \"hello 112\";\n        ngx.say(string.find(s, \"%d+$\"))';\n\n    content_by_lua 'ngx.exit(ngx.OK)';\n}\n--- request\nGET /set\n--- response_body\n79\n\n\n\n=== TEST 20: subrequests do not share variables of main requests by default\n--- config\nlocation /sub {\n    echo $a;\n}\nlocation /parent {\n    set $a 12;\n    access_by_lua 'local res = ngx.location.capture(\"/sub\"); ngx.print(res.body)';\n    content_by_lua 'ngx.exit(ngx.OK)';\n}\n--- request\nGET /parent\n--- response_body eval: \"\\n\"\n\n\n\n=== TEST 21: subrequests can share variables of main requests\n--- config\nlocation /sub {\n    echo $a;\n}\nlocation /parent {\n    set $a '';\n    access_by_lua '\n        ngx.var.a = 12;\n        local res = ngx.location.capture(\n            \"/sub\",\n            { share_all_vars = true }\n        );\n        ngx.print(res.body)\n    ';\n    content_by_lua 'ngx.exit(ngx.OK)';\n}\n--- request\nGET /parent\n--- response_body\n12\n\n\n\n=== TEST 22: main requests use subrequests' variables\n--- config\nlocation /sub {\n    set $a 12;\n}\nlocation /parent {\n    access_by_lua '\n        local res = ngx.location.capture(\"/sub\", { share_all_vars = true });\n        ngx.say(ngx.var.a)\n    ';\n\n    content_by_lua 'ngx.exit(ngx.OK)';\n}\n--- request\nGET /parent\n--- response_body\n12\n\n\n\n=== TEST 23: main requests do NOT use subrequests' variables\n--- config\nlocation /sub {\n    set $a 12;\n    content_by_lua return;\n}\n\nlocation /parent {\n    access_by_lua '\n        local res = ngx.location.capture(\"/sub\", { share_all_vars = false });\n        ngx.say(ngx.var.a)\n    ';\n    content_by_lua return;\n}\n--- request\nGET /parent\n--- response_body_like eval: \"\\n\"\n\n\n\n=== TEST 24: capture location headers\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo \"hello, world\";\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/other\");\n            ngx.say(\"type: \", res.header[\"Content-Type\"]);\n        ';\n\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\ntype: foo/bar\n\n\n\n=== TEST 25: capture location headers\n--- config\n    location /other {\n        default_type 'foo/bar';\n        rewrite_by_lua '\n            ngx.header.Bar = \"Bah\";\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/other\");\n            ngx.say(\"type: \", res.header[\"Content-Type\"]);\n            ngx.say(\"Bar: \", res.header[\"Bar\"]);\n        ';\n\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\ntype: foo/bar\nBar: Bah\n\n\n\n=== TEST 26: capture location headers\n--- config\n    location /other {\n        default_type 'foo/bar';\n        access_by_lua '\n            ngx.header.Bar = \"Bah\";\n            ngx.header.Bar = nil;\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/other\");\n            ngx.say(\"type: \", res.header[\"Content-Type\"]);\n            ngx.say(\"Bar: \", res.header[\"Bar\"] or \"nil\");\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\ntype: foo/bar\nBar: nil\n\n\n\n=== TEST 27: access_by_lua runs after ngx_access\n--- config\n    location /lua {\n        deny all;\n\n        access_by_lua '\n            ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)\n        ';\n\n        content_by_lua return;\n    }\n--- request\nGET /lua\n--- response_body_like: 403 Forbidden\n--- error_code: 403\n\n\n\n=== TEST 28: auth_request runs before ngx_access\n--- config\n    location /lua {\n        deny all;\n\n        auth_request /auth;\n\n        content_by_lua return;\n    }\n--- request\nGET /lua\n--- response_body_like: 403 Forbidden\n--- error_code: 403\n--- SKIP\n\n\n\n=== TEST 29: access_by_lua shouldn't send headers automatically (on simple return)\n--- config\n    location /lua {\n        access_by_lua 'return';\n\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n\n    location = /foo {\n        default_type 'text/css';\n        add_header Bar Baz;\n        echo foo;\n    }\n--- request\nGET /lua\n--- response_headers\nBar: Baz\nContent-Type: text/css\n--- response_body\nfoo\n\n\n\n=== TEST 30: access_by_lua shouldn't send headers automatically (on simple exit)\n--- config\n    location /lua {\n        access_by_lua 'ngx.exit(ngx.OK)';\n\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n\n    location = /foo {\n        default_type 'text/css';\n        add_header Bar Baz;\n        echo foo;\n    }\n--- request\nGET /lua\n--- response_headers\nBar: Baz\nContent-Type: text/css\n--- response_body\nfoo\n\n\n\n=== TEST 31: short circuit\n--- config\n    location /lua {\n        access_by_lua '\n            ngx.say(\"Hi\")\n            ngx.eof()\n            ngx.exit(ngx.HTTP_OK)\n        ';\n\n        content_by_lua '\n            print(\"HERE\")\n            ngx.print(\"BAD\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nHi\n\n\n\n=== TEST 32: nginx vars in script path\n--- config\n    location ~ ^/lua/(.+)$ {\n        access_by_lua_file html/$1.lua;\n\n        content_by_lua '\n            print(\"HERE\")\n            ngx.print(\"BAD\")\n        ';\n    }\n--- user_files\n>>> hi.lua\nngx.say(\"Hi\")\nngx.eof()\nngx.exit(ngx.HTTP_OK)\n--- request\nGET /lua/hi\n--- response_body\nHi\n\n\n\n=== TEST 33: phase postponing works for various locations (access phase not running in subrequest)\n--- config\n    location ~ '^/lua/(.+)' {\n        set $path $1;\n        access_by_lua 'ngx.say(ngx.var.path)';\n        content_by_lua return;\n    }\n    location ~ '^/lua2/(.+)' {\n        set $path $1;\n        access_by_lua 'ngx.say(ngx.var.path)';\n        content_by_lua return;\n    }\n    location /main {\n        echo_location /lua/foo;\n        echo_location /lua/bar;\n        echo_location /lua2/baz;\n        echo_location /lua2/bah;\n    }\n--- request\nGET /main\n--- response_body\n\n\n\n=== TEST 34: server access_by_lua\n--- config\n    access_by_lua 'ngx.header[\"X-Foo\"] = \"bar\" -- ngx.send_headers()';\n--- request\nGET /\n--- response_body chop\n<html><head><title>It works!</title></head><body>It works!</body></html>\n--- response_headers\nX-Foo: bar\n\n\n\n=== TEST 35: server access_by_lua_file\n--- config\n    access_by_lua_file html/foo.lua;\n--- user_files\n>>> foo.lua\nngx.header[\"X-Foo\"] = \"bar\" -- ngx.send_headers()\n--- request\nGET /\n--- response_body chop\n<html><head><title>It works!</title></head><body>It works!</body></html>\n--- response_headers\nX-Foo: bar\n\n\n\n=== TEST 36: Lua file does not exist\n--- config\n    location /lua {\n        access_by_lua_file html/test2.lua;\n    }\n--- user_files\n>>> test.lua\nv = ngx.var[\"request_uri\"]\nngx.print(\"request_uri: \", v, \"\\n\")\n--- request\nGET /lua?a=1&b=2\n--- response_body_like: 404 Not Found\n--- error_code: 404\n--- error_log eval\nqr/failed to load external Lua file \".*?test2\\.lua\": cannot open .*? No such file or directory/\n\n\n\n=== TEST 37: use of ngx.say() in access_by_lua without exiting with 200+.\n--- config\n    location /t {\n        access_by_lua \"ngx.say('test')\";\n        echo_exec /t2;\n    }\n--- request\n    GET /t\n--- response_body\ntest\n--- no_error_log\n[alert]\n\n\n\n=== TEST 38: use of ngx.say() in access_by_lua without exiting with 200+. (with explicit ngx.eof())\n--- config\n    location /t {\n        access_by_lua \"ngx.say('test') ngx.eof()\";\n        echo_exec /t2;\n    }\n--- request\n    GET /t\n--- response_body\ntest\n--- no_error_log\n[alert]\n\n\n\n=== TEST 39: use of ngx.say() in access_by_lua without exiting with 200+. (with IO)\n--- config\n    location /t {\n        access_by_lua \"ngx.say('test') ngx.sleep(0.001)\";\n        echo_exec /t2;\n    }\n--- request\n    GET /t\n--- response_body\ntest\n--- no_error_log\n[alert]\n"
  },
  {
    "path": "t/024-access/satisfy.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\nworker_connections(1014);\n#master_on();\n#workers(4);\n#log_level('warn');\nno_root_location();\n\n#repeat_each(2);\n#repeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\nour $HtmlDir = html_dir;\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: satisfy any\n--- config\n    location /test {\n        satisfy any;\n        allow all;\n        access_by_lua 'ngx.exit(403)';\n\n        echo something important;\n    }\n--- request\n    GET /test\n--- more_headers\n--- response_body\nsomething important\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: satisfy any\n--- config\n    location /test {\n        satisfy any;\n        deny all;\n        access_by_lua 'ngx.exit(403)';\n\n        echo something important;\n    }\n--- request\n    GET /test\n--- more_headers\n--- response_body_like: 403 Forbidden\n--- error_code: 403\n--- error_log\naccess forbidden by rule\n\n\n\n=== TEST 3: satisfy any (explicit ngx.exit(0))\n--- config\n    location /test {\n        satisfy any;\n        deny all;\n        access_by_lua 'ngx.exit(0)';\n\n        echo something important;\n    }\n--- request\n    GET /test\n--- more_headers\n--- response_body\nsomething important\n--- error_code: 200\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: satisfy any (simple return)\n--- config\n    location /test {\n        satisfy any;\n        deny all;\n        access_by_lua return;\n\n        echo something important;\n    }\n--- request\n    GET /test\n--- more_headers\n--- response_body\nsomething important\n--- error_code: 200\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: satisfy any (declined)\n--- config\n    location /test {\n        satisfy any;\n        deny all;\n        access_by_lua 'ngx.exit(ngx.DECLINED)';\n\n        echo something important;\n    }\n--- request\n    GET /test\n--- more_headers\n--- response_body_like: 403 Forbidden\n--- error_code: 403\n--- error_log\naccess forbidden by rule\n\n\n\n=== TEST 6: satisfy any (declined, with I/O)\n--- config\n    location /test {\n        satisfy any;\n        deny all;\n        access_by_lua 'ngx.location.capture(\"/echo\") ngx.exit(ngx.DECLINED)';\n\n        echo something important;\n    }\n\n    location /echo {\n        echo hi;\n        #echo_sleep 0.01;\n    }\n--- request\n    GET /test\n--- more_headers\n--- response_body_like: 403 Forbidden\n--- error_code: 403\n--- error_log\naccess forbidden by rule\n\n\n\n=== TEST 7: satisfy any (simple return, with I/O)\n--- config\n    location /test {\n        satisfy any;\n        deny all;\n        access_by_lua 'ngx.location.capture(\"/echo\") return';\n\n        echo something important;\n    }\n\n    location /echo {\n        echo hi;\n    }\n--- request\n    GET /test\n--- more_headers\n--- response_body\nsomething important\n--- error_code: 200\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: satisfy any - with I/O\n--- config\n    location /test {\n        satisfy any;\n        deny all;\n        access_by_lua 'ngx.location.capture(\"/echo\") ngx.exit(403)';\n\n        echo something important;\n    }\n\n    location /echo {\n        echo hi;\n    }\n--- request\n    GET /test\n--- more_headers\n--- response_body_like: 403 Forbidden\n--- error_code: 403\n--- error_log\naccess forbidden by rule\n\n\n\n=== TEST 9: satisfy any (explicit ngx.exit(0), with I/O)\n--- config\n    location /test {\n        satisfy any;\n        deny all;\n        access_by_lua 'ngx.location.capture(\"/echo\") ngx.exit(0)';\n\n        echo something important;\n    }\n\n    location /echo {\n        echo hi;\n    }\n--- request\n    GET /test\n--- more_headers\n--- response_body\nsomething important\n--- error_code: 200\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/024-access/sleep.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\nlog_level('debug');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 33;\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sleep 0.5\n--- config\n    location /test {\n        access_by_lua '\n            ngx.update_time()\n            local before = ngx.now()\n            ngx.sleep(0.5)\n            local now = ngx.now()\n            ngx.say(now - before)\n            ngx.exit(200)\n        ';\n    }\n--- request\nGET /test\n--- response_body_like chop\n^0\\.(?:4[5-9]\\d*|5[0-5]\\d*|5)$\n--- error_log\nlua ready to sleep for\nlua sleep timer expired: \"/test?\"\n\n\n\n=== TEST 2: sleep ag\n--- config\n    location /test {\n        access_by_lua '\n            ngx.update_time()\n            local before = ngx.now()\n            ngx.sleep(\"a\")\n            local now = ngx.now()\n            ngx.say(now - before)\n            ngx.exit(200)\n        ';\n    }\n--- request\nGET /test\n--- error_code: 500\n--- response_body_like: 500 Internal Server Error\n--- error_log\nbad argument #1 to 'sleep'\n\n\n\n=== TEST 3: sleep 0.5 in subrequest\n--- config\n    location /test {\n        access_by_lua '\n            ngx.update_time()\n            local before = ngx.now()\n            ngx.location.capture(\"/sleep\")\n            local now = ngx.now()\n            local delay = now - before\n            ngx.say(delay)\n            ngx.exit(200)\n        ';\n    }\n    location /sleep {\n        content_by_lua 'ngx.sleep(0.5)';\n    }\n--- request\nGET /test\n--- response_body_like chop\n^0\\.(?:4[5-9]\\d*|5[0-9]\\d*|5)$\n--- error_log\nlua ready to sleep for\nlua sleep timer expired: \"/sleep?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: sleep a in subrequest with bad argument\n--- config\n    location /test {\n        access_by_lua '\n            local res = ngx.location.capture(\"/sleep\");\n            ngx.say(res.status)\n            ngx.exit(200)\n        ';\n    }\n    location /sleep {\n        content_by_lua 'ngx.sleep(\"a\")';\n    }\n--- request\nGET /test\n--- response_body\n500\n--- error_log\nbad argument #1 to 'sleep'\n\n\n\n=== TEST 5: sleep 0.5 - multi-times\n--- quic_max_idle_timeout: 1.0\n--- config\n    location /test {\n        access_by_lua '\n            ngx.update_time()\n            local start = ngx.now()\n            ngx.sleep(0.3)\n            ngx.sleep(0.3)\n            ngx.sleep(0.3)\n            ngx.say(ngx.now() - start)\n            ngx.exit(200)\n        ';\n    }\n--- request\nGET /test\n--- response_body_like chop\n^0\\.(?:8[5-9]\\d*|9[0-9]\\d*|9)$\n--- error_log\nlua ready to sleep for\nlua sleep timer expired: \"/test?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: sleep 0.5 - interleaved by ngx.say() - ended by ngx.sleep\n--- quic_max_idle_timeout: 2.2\n--- config\n    location /test {\n        access_by_lua '\n            ngx.send_headers()\n            -- ngx.location.capture(\"/sleep\")\n            ngx.sleep(1)\n            ngx.say(\"blah\")\n            ngx.sleep(1)\n            -- ngx.location.capture(\"/sleep\")\n            ngx.exit(200)\n        ';\n    }\n    location = /sleep {\n        echo_sleep 0.1;\n    }\n--- request\nGET /test\n--- response_body\nblah\n--- error_log\nlua ready to sleep\nlua sleep timer expired: \"/test?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: sleep 0.5 - interleaved by ngx.say() - not ended by ngx.sleep\n--- quic_max_idle_timeout: 0.85\n--- config\n    location /test {\n        access_by_lua '\n            ngx.send_headers()\n            -- ngx.location.capture(\"/sleep\")\n            ngx.sleep(0.3)\n            ngx.say(\"blah\")\n            ngx.sleep(0.5)\n            -- ngx.location.capture(\"/sleep\")\n            ngx.say(\"hiya\")\n            ngx.exit(200)\n        ';\n    }\n    location = /sleep {\n        echo_sleep 0.1;\n    }\n--- request\nGET /test\n--- response_body\nblah\nhiya\n--- error_log\nlua ready to sleep for\nlua sleep timer expired: \"/test?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: ngx.location.capture before and after ngx.sleep\n--- config\n    location /test {\n        access_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n\n            ngx.sleep(0.1)\n\n            res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n            ngx.exit(200)\n        ';\n    }\n    location = /hello {\n        echo hello world;\n    }\n    location = /sub {\n        proxy_pass http://127.0.0.1:$server_port/hello;\n    }\n--- request\nGET /test\n--- response_body\nhello world\nhello world\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/024-access/subrequest.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 2);\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: DELETE\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_DELETE });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nDELETE\n\n\n\n=== TEST 2: DELETE (proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_DELETE });\n\n            ngx.print(res.body)\n        ';\n\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nDELETE\n\n\n\n=== TEST 3: POST (nobody, proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_POST });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nPOST\n\n\n\n=== TEST 4: HEAD\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_HEAD });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\n\n\n\n=== TEST 5: explicit GET\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_GET });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nGET\n\n\n\n=== TEST 6: implicit GET\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\")\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nGET\n\n\n\n=== TEST 7: implicit GET (empty option table)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo $echo_request_method;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\", {})\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nGET\n\n\n\n=== TEST 8: PUT (nobody, proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo_read_request_body;\n\n        echo $echo_request_method;\n        echo_request_body;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body chomp\nPUT\nhello\n\n\n\n=== TEST 9: PUT (nobody, no proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        #echo_read_request_body;\n\n        echo $echo_request_method;\n        #echo $echo_request_body;\n        echo_request_body;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body chomp\nPUT\nhello\n\n\n\n=== TEST 10: PUT (nobody, no proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        #echo_read_request_body;\n\n        echo $echo_request_method;\n        #echo $echo_request_body;\n        echo_request_body;\n        #echo \"[$http_content_length]\";\n        echo;\n    }\n\n    location /foo {\n        echo $echo_request_method;\n        echo -n \"[$http_content_length]\";\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n\n            ngx.print(res.body)\n\n            res = ngx.location.capture(\"/foo\")\n            ngx.say(res.body)\n\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nPUT\nhello\nGET\n[]\n\n\n\n=== TEST 11: POST (with body, proxy method)\n--- config\n    location /other {\n        default_type 'foo/bar';\n        echo_read_request_body;\n\n        echo $echo_request_method;\n        echo_request_body;\n    }\n\n    location /foo {\n        proxy_pass http://127.0.0.1:$server_port/other;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { method = ngx.HTTP_POST, body = \"hello\" });\n\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body chomp\nPOST\nhello\n\n\n\n=== TEST 12: POST (with body, memc method)\n--- config\n    location /flush {\n        set $memc_cmd flush_all;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /memc {\n        set $memc_key $echo_request_uri;\n        set $memc_exptime 600;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /lua {\n        access_by_lua '\n            ngx.location.capture(\"/flush\");\n\n            local res = ngx.location.capture(\"/memc\");\n            ngx.say(\"GET: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello\" });\n            ngx.say(\"PUT: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\");\n            ngx.say(\"cached: \" .. res.body);\n\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nGET: 404\nPUT: 201\ncached: hello\n\n\n\n=== TEST 13: POST (with body, memc method)\n--- config\n    location /flush {\n        set $memc_cmd flush_all;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /memc {\n        set $memc_cmd \"\";\n        set $memc_key $echo_request_uri;\n        set $memc_exptime 600;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location /lua {\n        access_by_lua '\n            ngx.location.capture(\"/flush\",\n                { share_all_vars = true });\n\n            local res = ngx.location.capture(\"/memc\",\n                { share_all_vars = true });\n            ngx.say(\"GET: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\",\n                { method = ngx.HTTP_PUT, body = \"hello\", share_all_vars = true });\n            ngx.say(\"PUT: \" .. res.status);\n\n            res = ngx.location.capture(\"/memc\", { share_all_vars = true });\n            ngx.say(\"cached: \" .. res.body);\n\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nGET: 404\nPUT: 201\ncached: hello\n\n\n\n=== TEST 14: empty args option table\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = {} })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body eval: \"\\n\"\n\n\n\n=== TEST 15: non-empty args option table (1 pair)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { [\"fo=\"] = \"=>\" } })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\nfo%3D=%3D%3E\n\n\n\n=== TEST 16: non-empty args option table (2 pairs)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { [\"fo=\"] = \"=>\",\n                    [\"=\"] = \":\" } })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body_like chop\n^(?:fo%3D=%3D%3E\\&%3D=%3A|%3D=%3A\\&fo%3D=%3D%3E)$\n\n\n\n=== TEST 17: non-empty args option table (2 pairs, no special chars)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { foo = 3,\n                    bar = \"hello\" } })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body_like chop\n^(?:bar=hello\\&foo=3|foo=3\\&bar=hello)$\n\n\n\n=== TEST 18: non-empty args option table (number key)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { [57] = \"hi\" } })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 19: non-empty args option table (plain arrays)\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo\",\n                { args = { \"hi\" } })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 20: more args\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo?a=3\",\n                { args = { b = 4 } })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\na=3&b=4\n\n\n\n=== TEST 21: more args\n--- config\n    location /foo {\n        echo $query_string;\n    }\n\n    location /lua {\n        access_by_lua '\n            local res = ngx.location.capture(\"/foo?a=3\",\n                { args = \"b=4\" })\n            ngx.print(res.body)\n        ';\n        content_by_lua 'ngx.exit(ngx.OK)';\n    }\n--- request\nGET /lua\n--- response_body\na=3&b=4\n\n\n\n=== TEST 22: I/O in named location\nthe nginx core requires the patch https://github.com/agentzh/ngx_openresty/blob/master/patches/nginx-1.0.15-reset_wev_handler_in_named_locations.patch\n--- config\n    location /t {\n        echo_exec @named;\n    }\n\n    location @named {\n        access_by_lua '\n            ngx.location.capture(\"/hello\")\n        ';\n        echo done;\n    }\n\n    location /hello {\n        echo hello;\n    }\n--- request\n    GET /t\n--- response_body\ndone\n"
  },
  {
    "path": "t/024-access/uthread-exec.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: exec in user thread (entry still pending)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.exec(\"/foo\")\n            end\n\n            ngx.thread.spawn(f)\n            ngx.sleep(1)\n            ngx.say(\"hello\")\n        ';\n        content_by_lua return;\n    }\n\n    location /foo {\n        echo i am foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\ni am foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: exec in user thread (entry already quits)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exec(\"/foo\")\n            end\n\n            ngx.thread.spawn(f)\n        ';\n        content_by_lua return;\n    }\n\n    location /foo {\n        echo i am foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\ni am foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: exec in user thread (entry thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exec(\"/foo\")\n            end\n\n            ngx.thread.spawn(f)\n            ngx.sleep(1)\n        ';\n        content_by_lua return;\n    }\n\n    location = /foo {\n        echo hello foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 1000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nhello foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: exec in a user thread (another user thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exec(\"/foo\")\n            end\n\n            local function g()\n                ngx.sleep(1)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.thread.spawn(g)\n        ';\n        content_by_lua return;\n    }\n\n    location = /foo {\n        echo hello foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\ncreate 3 in 1\nspawn user thread 3 in 1\nadd timer 1000\nterminate 1: ok\ndelete thread 1\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 3\nfree request\n\n--- response_body\nhello foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: exec in user thread (entry thread is still pending on ngx.location.capture), without pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        access_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exec(\"/foo\")\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.location.capture(\"/sleep\")\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n\n    location = /foo {\n        echo hello world;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nend\n--- error_log\nattempt to abort with pending subrequests\n"
  },
  {
    "path": "t/024-access/uthread-exit.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: exit in user thread (entry thread is still pending to run)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            ngx.sleep(1)\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nM(timer-add) {\n    if ($arg2 == 1000) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nbefore\nhello in thread\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: exit in user thread (entry thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            ngx.sleep(1)\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 1000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: exit in a user thread (another user thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.say(\"f\")\n                ngx.exit(0)\n            end\n\n            local function g()\n                ngx.sleep(1)\n                ngx.say(\"g\")\n            end\n\n            ngx.thread.spawn(f)\n            ngx.thread.spawn(g)\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\ncreate 3 in 1\nspawn user thread 3 in 1\nadd timer 1000\nterminate 1: ok\ndelete thread 1\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 3\nfree request\n\n--- response_body\nend\nf\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: exit in user thread (entry already quits)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.say(\"exiting the user thread\")\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- wait: 0.1\n--- response_body\nbefore\nafter\nexiting the user thread\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: exit in user thread (entry thread is still pending on the DNS resolver for ngx.socket.tcp)\n--- config\n    location /lua {\n        resolver 127.0.0.2:12345;\n        resolver_timeout 12s;\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.001)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"agentzh.org\", 80)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nF(ngx_resolve_name) {\n    printf(\"resolving %s\\n\", user_string_n($ctx->name->data, $ctx->name->len))\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 1) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 1) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 12000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 1) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_tcp_resolve_cleanup) {\n    println(\"lua tcp resolve cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 1\nresolving agentzh.org\nadd timer 12000\nexpire timer 1\nterminate 2: ok\ndelete thread 2\nlua tcp resolve cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: exit in user thread (entry thread is still pending on the DNS resolver for ngx.socket.udp)\n--- config\n    location /lua {\n        resolver 127.0.0.2:12345;\n        resolver_timeout 12s;\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.001)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.udp()\n            local ok, err = sock:setpeername(\"agentzh.org\", 80)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nF(ngx_resolve_name) {\n    printf(\"resolving %s\\n\", user_string_n($ctx->name->data, $ctx->name->len))\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 1) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 1) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 12000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 1) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_udp_resolve_cleanup) {\n    println(\"lua udp resolve cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 1\nresolving agentzh.org\nadd timer 12000\nexpire timer 1\nterminate 2: ok\ndelete thread 2\nlua udp resolve cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: exit in user thread (entry thread is still pending on tcpsock:connect)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.tcp()\n            sock:settimeout(12000)\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 12000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua tcp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: exit in user thread (entry thread is still pending on tcpsock:receive)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, ok = sock:send(\"blpop not_exists 2\\\\r\\\\n\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            sock:settimeout(12000)\n\n            local data, err = sock:receive()\n            if not data then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua tcp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: exit in user thread (entry thread is still pending on tcpsock:receiveuntil's iterator)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, ok = sock:send(\"blpop not_exists 2\\\\r\\\\n\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local it, err = sock:receiveuntil(\"\\\\r\\\\n\")\n            if not it then\n                ngx.say(\"failed to receive until: \", err)\n                return\n            end\n\n            sock:settimeout(12000)\n\n            local data, err = it()\n            if not data then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua tcp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: exit in user thread (entry thread is still pending on udpsock:receive)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.udp()\n\n            local ok, err = sock:setpeername(\"8.8.8.8\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            sock:settimeout(12000)\n\n            local data, err = sock:receive()\n            if not data then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_udp_socket_cleanup) {\n    println(\"lua udp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua udp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: exit in user thread (entry thread is still pending on reqsock:receive)\n--- skip_eval: 5:$ENV{TEST_NGINX_USE_HTTP3}\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.req.socket()\n\n            sock:settimeout(12000)\n\n            local data, err = sock:receive(1024)\n            if not data then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nPOST /lua\n\n--- more_headers\nContent-Length: 1024\n\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua tcp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: exit in user thread (entry thread is still pending on ngx.req.read_body)\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n\n            ngx.req.read_body()\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nPOST /lua\n--- more_headers\nContent-Length: 1024\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_req_body_cleanup) {\n    println(\"lua req body cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua req body cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: exit in user thread (entry thread is still pending on ngx.location.capture), with pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nbefore\nhello in thread\nafter\nend\n--- error_log\nattempt to abort with pending subrequests\n\n\n\n=== TEST 14: exit in user thread (entry thread is still pending on ngx.location.capture), without pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        access_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.location.capture(\"/sleep\")\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq %s\\n\", ngx_http_req_uri($r))\n}\n\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\npost subreq /sleep\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nend\n--- error_log\nattempt to abort with pending subrequests\n\n\n\n=== TEST 15: exit in user thread (entry thread is still pending on ngx.location.capture_multi), without pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        access_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.location.capture_multi{\n                {\"/echo\"},\n                {\"/sleep\"}\n            }\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /echo {\n        echo hello;\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq %s\\n\", ngx_http_req_uri($r))\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\npost subreq /echo\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\npost subreq /sleep\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nend\n--- error_log\nattempt to abort with pending subrequests\n"
  },
  {
    "path": "t/024-access/uthread-redirect.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: ngx.redirect() in user thread (entry thread is still pending on ngx.location.capture_multi), without pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        access_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.redirect(301)\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.location.capture_multi{\n                {\"/echo\"},\n                {\"/sleep\"}\n            }\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /echo {\n        echo hello;\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq %s\\n\", ngx_http_req_uri($r))\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\npost subreq /echo\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\npost subreq /sleep\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- response_body\nend\n--- error_log\nattempt to abort with pending subrequests\n\n\n\n=== TEST 2: redirect in user thread (entry thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.redirect(301)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.sleep(1)\n            ngx.say(\"end\")\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 1000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body_like: 302 Found\n--- error_code: 302\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/024-access/uthread-spawn.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4 + 1);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple user thread without I/O\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval\n<<'_EOC_' . $::StapScript;\n\nF(ngx_http_lua_send_chain_link) {\n    printf(\"send link %p\\n\", $in)\n}\n\nF(ngx_http_core_content_phase) {\n    println(\"core content phase\")\n}\n\n_EOC_\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: two simple user threads without I/O\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"in thread 1\")\n            end\n\n            local function g()\n                ngx.say(\"in thread 2\")\n            end\n\n            ngx.say(\"before 1\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after 1\")\n\n            ngx.say(\"before 2\")\n            ngx.thread.spawn(g)\n            ngx.say(\"after 2\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 3: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 3\ndelete thread 1\n\n--- response_body\nbefore 1\nin thread 1\nafter 1\nbefore 2\nin thread 2\nafter 2\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: simple user thread with sleep\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"before sleep\")\n                ngx.sleep(0.1)\n                ngx.say(\"after sleep\")\n            end\n\n            ngx.say(\"before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread create\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore thread create\nbefore sleep\nafter thread create\nafter sleep\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: two simple user threads with sleep\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"1: before sleep\")\n                ngx.sleep(0.2)\n                ngx.say(\"1: after sleep\")\n            end\n\n            local function g()\n                ngx.say(\"2: before sleep\")\n                ngx.sleep(0.1)\n                ngx.say(\"2: after sleep\")\n            end\n\n            ngx.say(\"1: before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"1: after thread create\")\n\n            ngx.say(\"2: before thread create\")\n            ngx.thread.spawn(g)\n            ngx.say(\"2: after thread create\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 3: ok\ndelete thread 3\nterminate 2: ok\ndelete thread 2\n\n--- wait: 0.1\n--- response_body\n1: before thread create\n1: before sleep\n1: after thread create\n2: before thread create\n2: before sleep\n2: after thread create\n2: after sleep\n1: after sleep\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: error in user thread\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.blah()\n            end\n\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: fail\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nafter\n--- error_log eval\nqr/lua user thread aborted: runtime error: access_by_lua\\(nginx\\.conf:\\d+\\):3: attempt to call field 'blah' \\(a nil value\\)/\n\n\n\n=== TEST 6: simple user threads doing a single subrequest (entry quits early)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"before capture\")\n                local res = ngx.location.capture(\"/proxy\")\n                ngx.say(\"after capture: \", res.body)\n            end\n\n            ngx.say(\"before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread create\")\n        ';\n    }\n\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n\n    location /foo {\n        echo_sleep 0.1;\n        echo -n hello world;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore thread create\nbefore capture\nafter thread create\nafter capture: hello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: simple user threads doing a single subrequest (entry also does a subrequest and quits early)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"before capture\")\n                local res = ngx.location.capture(\"/proxy?foo\")\n                ngx.say(\"after capture: \", res.body)\n            end\n\n            ngx.say(\"before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread create\")\n            local res = ngx.location.capture(\"/proxy?bar\")\n            ngx.say(\"capture: \", res.body)\n        ';\n    }\n\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/$args;\n    }\n\n    location /foo {\n        echo_sleep 0.1;\n        echo -n hello foo;\n    }\n\n    location /bar {\n        echo -n hello bar;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore thread create\nbefore capture\nafter thread create\ncapture: hello bar\nafter capture: hello foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: simple user threads doing a single subrequest (entry also does a subrequest and quits late)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"before capture\")\n                local res = ngx.location.capture(\"/proxy?foo\")\n                ngx.say(\"after capture: \", res.body)\n            end\n\n            ngx.say(\"before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread create\")\n            local res = ngx.location.capture(\"/proxy?bar\")\n            ngx.say(\"capture: \", res.body)\n        ';\n        content_by_lua return;\n    }\n\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/$args;\n    }\n\n    location /foo {\n        echo_sleep 0.1;\n        echo -n hello foo;\n    }\n\n    location /bar {\n        echo_sleep 0.2;\n        echo -n hello bar;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nbefore thread create\nbefore capture\nafter thread create\nafter capture: hello foo\ncapture: hello bar\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: two simple user threads doing single subrequests (entry also does a subrequest and quits between)\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"f: before capture\")\n                local res = ngx.location.capture(\"/proxy?foo\")\n                ngx.say(\"f: after capture: \", res.body)\n            end\n\n            local function g()\n                ngx.say(\"g: before capture\")\n                local res = ngx.location.capture(\"/proxy?bah\")\n                ngx.say(\"g: after capture: \", res.body)\n            end\n\n            ngx.say(\"before thread 1 create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread 1 create\")\n\n            ngx.say(\"before thread 2 create\")\n            ngx.thread.spawn(g)\n            ngx.say(\"after thread 2 create\")\n\n            local res = ngx.location.capture(\"/proxy?bar\")\n            ngx.say(\"capture: \", res.body)\n        ';\n    }\n\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/$args;\n    }\n\n    location /foo {\n        echo_sleep 0.1;\n        echo -n hello foo;\n    }\n\n    location /bar {\n        echo_sleep 0.2;\n        echo -n hello bar;\n    }\n\n    location /bah {\n        echo_sleep 0.3;\n        echo -n hello bah;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nbefore thread 1 create\nf: before capture\nafter thread 1 create\nbefore thread 2 create\ng: before capture\nafter thread 2 create\nf: after capture: hello foo\ncapture: hello bar\ng: after capture: hello bah\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: nested user threads\n--- config\n    location /lua {\n        access_by_lua '\n            local g\n            local function f()\n                ngx.say(\"before g\")\n                ngx.thread.spawn(g)\n                ngx.say(\"after g\")\n            end\n\n            function g()\n                ngx.say(\"hello in g()\")\n            end\n\n            ngx.say(\"before f\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after f\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 3: ok\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 3\ndelete thread 2\n\n--- response_body\nbefore f\nbefore g\nhello in g()\nafter f\nafter g\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: nested user threads (with I/O)\n--- config\n    location /lua {\n        access_by_lua '\n            local g\n            local function f()\n                ngx.say(\"before g\")\n                ngx.thread.spawn(g)\n                ngx.say(\"after g\")\n            end\n\n            function g()\n                ngx.sleep(0.1)\n                ngx.say(\"hello in g()\")\n            end\n\n            ngx.say(\"before f\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after f\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nbefore f\nbefore g\nafter f\nafter g\nhello in g()\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: coroutine status of a running user thread\n--- config\n    location /lua {\n        access_by_lua '\n            local co\n            local function f()\n                co = coroutine.running()\n                ngx.sleep(0.1)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.say(\"status: \", coroutine.status(co))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nstatus: running\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: coroutine status of a dead user thread\n--- config\n    location /lua {\n        access_by_lua '\n            local co\n            local function f()\n                co = coroutine.running()\n            end\n\n            ngx.thread.spawn(f)\n            ngx.say(\"status: \", coroutine.status(co))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nstatus: zombie\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: coroutine status of a \"normal\" user thread\n--- config\n    location /lua {\n        access_by_lua '\n            local co\n            local g\n            local function f()\n                co = coroutine.running()\n                local co2 = coroutine.create(g)\n                coroutine.resume(co2)\n            end\n\n            function g()\n                ngx.sleep(0.1)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.say(\"status: \", coroutine.status(co))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 2\nterminate 1: ok\ndelete thread 1\nterminate 3: ok\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nstatus: normal\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: creating user threads in a user coroutine\n--- config\n    location /lua {\n        access_by_lua '\n            local g\n            local function f()\n                ngx.say(\"before g\")\n                ngx.thread.spawn(g)\n                ngx.say(\"after g\")\n            end\n\n            function g()\n                ngx.say(\"hello in g()\")\n            end\n\n            ngx.say(\"before f\")\n            local co = coroutine.create(f)\n            coroutine.resume(co)\n            ngx.say(\"after f\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 3: ok\nterminate 2: ok\ndelete thread 3\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nbefore f\nbefore g\nhello in g()\nafter g\nafter f\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: manual time slicing between a user thread and the entry thread\n--- config\n    location /lua {\n        access_by_lua '\n            local yield = coroutine.yield\n\n            local function f()\n                local self = coroutine.running()\n                ngx.say(\"f 1\")\n                yield(self)\n                ngx.say(\"f 2\")\n                yield(self)\n                ngx.say(\"f 3\")\n            end\n\n            local self = coroutine.running()\n            ngx.say(\"0\")\n            yield(self)\n            ngx.say(\"1\")\n            ngx.thread.spawn(f)\n            ngx.say(\"2\")\n            yield(self)\n            ngx.say(\"3\")\n            yield(self)\n            ngx.say(\"4\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\n0\n1\nf 1\n2\nf 2\n3\nf 3\n4\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: manual time slicing between two user threads\n--- config\n    location /lua {\n        access_by_lua '\n            local yield = coroutine.yield\n\n            local function f()\n                local self = coroutine.running()\n                ngx.say(\"f 1\")\n                yield(self)\n                ngx.say(\"f 2\")\n                yield(self)\n                ngx.say(\"f 3\")\n            end\n\n            local function g()\n                local self = coroutine.running()\n                ngx.say(\"g 1\")\n                yield(self)\n                ngx.say(\"g 2\")\n                yield(self)\n                ngx.say(\"g 3\")\n            end\n\n            ngx.thread.spawn(f)\n            ngx.thread.spawn(g)\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nf 1\ng 1\nf 2\ndone\ng 2\nf 3\ng 3\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: entry thread and a user thread flushing at the same time\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                coroutine.yield(coroutine.running)\n                ngx.flush(true)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            ngx.flush(true)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: two user threads flushing at the same time\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.say(\"hello from f\")\n                ngx.flush(true)\n            end\n\n            local function g()\n                ngx.say(\"hello from g\")\n                ngx.flush(true)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.thread.spawn(g)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like\n^(?:create 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3|create 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 3: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 3\ndelete thread 1)$\n\n--- response_body\nhello from f\nhello from g\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: user threads + ngx.socket.tcp\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n                local bytes, err = sock:send(\"flush_all\\\\r\\\\n\")\n                if not bytes then\n                    ngx.say(\"failed to send query: \", err)\n                    return\n                end\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore\nafter\nreceived: OK\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: user threads + ngx.socket.udp\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                local sock = ngx.socket.udp()\n                local ok, err = sock:setpeername(\"127.0.0.1\", 12345)\n                local bytes, err = sock:send(\"blah\")\n                if not bytes then\n                    ngx.say(\"failed to send query: \", err)\n                    return\n                end\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like chop\n^(?:create 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n|create 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1)$\n\n--- udp_listen: 12345\n--- udp_query: blah\n--- udp_reply: hello udp\n--- response_body_like chop\n^(?:before\nafter\nreceived: hello udp\n|before\nreceived: hello udp\nafter)$\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: simple user thread with ngx.req.read_body()\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                ngx.req.read_body()\n                local body = ngx.req.get_body_data()\n                ngx.say(\"body: \", body)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nPOST /lua\nhello world\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like chop\n^(?:create 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1|create 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2)$\n\n--- response_body_like chop\n^(?:before\nbody: hello world\nafter|before\nafter\nbody: hello world)$\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: simple user thread with ngx.req.socket()\n--- config\n    location /lua {\n        access_by_lua '\n            local function f()\n                local sock = ngx.req.socket()\n                local body, err = sock:receive(11)\n                if not body then\n                    ngx.say(\"failed to read body: \", err)\n                    return\n                end\n\n                ngx.say(\"body: \", body)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nPOST /lua\nhello world\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like chop\n^(?:create 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1|create 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2)$\n\n--- response_body_like chop\n^(?:before\nbody: hello world\nafter|before\nafter\nbody: hello world)$\n\n--- no_error_log\n[error]\n--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3}\n"
  },
  {
    "path": "t/025-codecache.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse Cwd qw(abs_path realpath);\nuse File::Basename;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 198;\n\n#$ENV{LUA_PATH} = $ENV{HOME} . '/work/JSON4Lua-0.9.30/json/?.lua';\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n$ENV{TEST_NGINX_CERT_DIR} ||= dirname(realpath(abs_path(__FILE__)));\n\nno_long_string();\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\ncheck_accum_error_log();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: code cache on by default\n--- config\n    location /lua {\n        content_by_lua_file html/test.lua;\n    }\n    location /update {\n        content_by_lua '\n            -- os.execute(\"(echo HERE; pwd) > /dev/stderr\")\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/test.lua\", \"w\"))\n            f:write(\"ngx.say(101)\")\n            f:close()\n            ngx.say(\"updated\")\n        ';\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /update;\n        echo_location /lua;\n    }\n--- user_files\n>>> test.lua\nngx.say(32)\n--- request\nGET /main\n--- response_body\n32\nupdated\n32\n--- no_error_log\n[alert]\n\n\n\n=== TEST 2: code cache explicitly on\n--- config\n    location /lua {\n        lua_code_cache on;\n        content_by_lua_file html/test.lua;\n    }\n    location /update {\n        content_by_lua '\n            -- os.execute(\"(echo HERE; pwd) > /dev/stderr\")\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/test.lua\", \"w\"))\n            f:write(\"ngx.say(101)\")\n            f:close()\n            ngx.say(\"updated\")\n        ';\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /update;\n        echo_location /lua;\n    }\n--- user_files\n>>> test.lua\nngx.say(32)\n--- request\nGET /main\n--- response_body\n32\nupdated\n32\n--- no_error_log\n[alert]\n\n\n\n=== TEST 3: code cache explicitly off\n--- config\n    location /lua {\n        lua_code_cache off;\n        content_by_lua_file html/test.lua;\n    }\n    location /update {\n        content_by_lua '\n            -- os.execute(\"(echo HERE; pwd) > /dev/stderr\")\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/test.lua\", \"w\"))\n            f:write(\"ngx.say(101)\")\n            f:close()\n            ngx.say(\"updated\")\n        ';\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /update;\n        echo_location /lua;\n    }\n--- user_files\n>>> test.lua\nngx.say(32)\n--- request\nGET /main\n--- response_body\n32\nupdated\n101\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 4: code cache explicitly off (server level)\n--- config\n    lua_code_cache off;\n\n    location /lua {\n        content_by_lua_file html/test.lua;\n    }\n    location /update {\n        content_by_lua '\n            -- os.execute(\"(echo HERE; pwd) > /dev/stderr\")\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/test.lua\", \"w\"))\n            f:write(\"ngx.say(101)\")\n            f:close()\n            ngx.say(\"updated\")\n        ';\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /update;\n        echo_location /lua;\n    }\n--- user_files\n>>> test.lua\nngx.say(32)\n--- request\nGET /main\n--- response_body\n32\nupdated\n101\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 5: code cache explicitly off (server level) but be overridden in the location\n--- config\n    lua_code_cache off;\n\n    location /lua {\n        lua_code_cache on;\n        content_by_lua_file html/test.lua;\n    }\n    location /update {\n        content_by_lua '\n            -- os.execute(\"(echo HERE; pwd) > /dev/stderr\")\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/test.lua\", \"w\"))\n            f:write(\"ngx.say(101)\")\n            f:close()\n            ngx.say(\"updated\")\n        ';\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /update;\n        echo_location /lua;\n    }\n--- user_files\n>>> test.lua\nngx.say(32)\n--- request\nGET /main\n--- response_body\n32\nupdated\n32\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 6: code cache explicitly off (affects require) + content_by_lua\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /lua {\n        lua_code_cache off;\n        content_by_lua '\n            local foo = require \"foo\";\n        ';\n    }\n    location /update {\n        content_by_lua '\n            -- os.execute(\"(echo HERE; pwd) > /dev/stderr\")\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/foo.lua\", \"w\"))\n            f:write(\"module(..., package.seeall); ngx.say(102);\")\n            f:close()\n            ngx.say(\"updated\")\n        ';\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /update;\n        echo_location /lua;\n    }\n--- user_files\n>>> foo.lua\nmodule(..., package.seeall); ngx.say(32);\n--- request\nGET /main\n--- response_body\n32\nupdated\n102\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 7: code cache explicitly off (affects require) + content_by_lua_file\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /lua {\n        lua_code_cache off;\n        content_by_lua_file html/test.lua;\n    }\n    location /update {\n        content_by_lua '\n            -- os.execute(\"(echo HERE; pwd) > /dev/stderr\")\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/foo.lua\", \"w\"))\n            f:write(\"module(..., package.seeall); ngx.say(102);\")\n            f:close()\n            ngx.say(\"updated\")\n        ';\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /update;\n        echo_location /lua;\n    }\n--- user_files\n>>> test.lua\nlocal foo = require \"foo\";\n>>> foo.lua\nmodule(..., package.seeall); ngx.say(32);\n--- request\nGET /main\n--- response_body\n32\nupdated\n102\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 8: code cache explicitly off (affects require) + set_by_lua_file\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /lua {\n        lua_code_cache off;\n        set_by_lua_file $a html/test.lua;\n        echo $a;\n    }\n    location /update {\n        content_by_lua '\n            -- os.execute(\"(echo HERE; pwd) > /dev/stderr\")\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/foo.lua\", \"w\"))\n            f:write(\"module(..., package.seeall); return 102;\")\n            f:close()\n            ngx.say(\"updated\")\n        ';\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /update;\n        echo_location /lua;\n    }\n--- user_files\n>>> test.lua\nreturn require \"foo\"\n>>> foo.lua\nmodule(..., package.seeall); return 32;\n--- request\nGET /main\n--- response_body\n32\nupdated\n102\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 9: code cache explicitly on (affects require) + set_by_lua_file\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /lua {\n        lua_code_cache on;\n        set_by_lua_file $a html/test.lua;\n        echo $a;\n    }\n    location /update {\n        content_by_lua '\n            -- os.execute(\"(echo HERE; pwd) > /dev/stderr\")\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/foo.lua\", \"w\"))\n            f:write(\"module(..., package.seeall); return 102;\")\n            f:close()\n            ngx.say(\"updated\")\n        ';\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /update;\n        echo_location /lua;\n    }\n--- user_files\n>>> test.lua\nreturn require \"foo\"\n>>> foo.lua\nmodule(..., package.seeall); return 32;\n--- request\nGET /main\n--- response_body\n32\nupdated\n32\n--- no_error_log\n[alert]\n\n\n\n=== TEST 10: code cache explicitly off + set_by_lua_file\n--- config\n    location /lua {\n        lua_code_cache off;\n        set_by_lua_file $a html/test.lua;\n        echo $a;\n    }\n    location /update {\n        content_by_lua '\n            -- os.execute(\"(echo HERE; pwd) > /dev/stderr\")\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/test.lua\", \"w\"))\n            f:write(\"return 101\")\n            f:close()\n            ngx.say(\"updated\")\n        ';\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /update;\n        echo_location /lua;\n    }\n--- user_files\n>>> test.lua\nreturn 32\n--- request\nGET /main\n--- response_body\n32\nupdated\n101\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 11: code cache explicitly on + set_by_lua_file\n--- config\n    location /lua {\n        lua_code_cache on;\n        set_by_lua_file $a html/test.lua;\n        echo $a;\n    }\n    location /update {\n        content_by_lua '\n            -- os.execute(\"(echo HERE; pwd) > /dev/stderr\")\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/test.lua\", \"w\"))\n            f:write(\"return 101\")\n            f:close()\n            ngx.say(\"updated\")\n        ';\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /update;\n        echo_location /lua;\n    }\n--- user_files\n>>> test.lua\nreturn 32\n--- request\nGET /main\n--- response_body\n32\nupdated\n32\n--- no_error_log\n[alert]\n\n\n\n=== TEST 12: no clear builtin lib \"string\"\n--- config\n    location /lua {\n        lua_code_cache off;\n        content_by_lua_file html/test.lua;\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /lua;\n    }\n--- user_files\n>>> test.lua\nngx.say(string.len(\"hello\"))\nngx.say(table.concat({1,2,3}, \", \"))\n--- request\n    GET /main\n--- response_body\n5\n1, 2, 3\n5\n1, 2, 3\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 13: no clear builtin lib \"string\"\n--- config\n    location /lua {\n        lua_code_cache off;\n        content_by_lua '\n            ngx.say(string.len(\"hello\"))\n            ngx.say(table.concat({1,2,3}, \", \"))\n        ';\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /lua;\n    }\n--- request\n    GET /main\n--- response_body\n5\n1, 2, 3\n5\n1, 2, 3\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 14: no clear builtin lib \"string\"\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    lua_code_cache off;\n    location /lua {\n        content_by_lua '\n            local test = require(\"test\")\n        ';\n    }\n    location /main {\n        echo_location /lua;\n        echo_location /lua;\n    }\n--- request\n    GET /main\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nstring = require(\"string\")\nmath = require(\"math\")\nio = require(\"io\")\nos = require(\"os\")\ntable = require(\"table\")\ncoroutine = require(\"coroutine\")\npackage = require(\"package\")\nngx.say(\"OK\")\n--- response_body\nOK\nOK\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 15: do not skip luarocks\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n     lua_code_cache off;\"\n--- config\n    location /main {\n        echo_location /load;\n        echo_location /check;\n        echo_location /check;\n    }\n\n    location /load {\n        content_by_lua '\n            package.loaded.luarocks = nil;\n            local foo = require \"luarocks\";\n            foo.hi()\n        ';\n    }\n\n    location /check {\n        content_by_lua '\n            local foo = package.loaded.luarocks\n            if foo then\n                ngx.say(\"found\")\n            else\n                ngx.say(\"not found\")\n            end\n        ';\n    }\n--- request\nGET /main\n--- user_files\n>>> luarocks.lua\nmodule(..., package.seeall);\n\nngx.say(\"loading\");\n\nfunction hi ()\n    ngx.say(\"hello, foo\")\nend;\n--- response_body\nloading\nhello, foo\nnot found\nnot found\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 16: do not skip luarocks*\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n     lua_code_cache off;\"\n--- config\n    location /main {\n        echo_location /load;\n        echo_location /check;\n        echo_location /check;\n    }\n\n    location /load {\n        content_by_lua '\n            package.loaded.luarocks2 = nil;\n            local foo = require \"luarocks2\";\n            foo.hi()\n        ';\n    }\n\n    location /check {\n        content_by_lua '\n            local foo = package.loaded.luarocks2\n            if foo then\n                ngx.say(\"found\")\n            else\n                ngx.say(\"not found\")\n            end\n        ';\n    }\n--- request\nGET /main\n--- user_files\n>>> luarocks2.lua\nmodule(..., package.seeall);\n\nngx.say(\"loading\");\n\nfunction hi ()\n    ngx.say(\"hello, foo\")\nend;\n--- response_body\nloading\nhello, foo\nnot found\nnot found\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 17: clear _G table\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    lua_code_cache off;\n    location /t {\n        content_by_lua '\n            if not _G.foo then\n                _G.foo = 1\n            else\n                _G.foo = _G.foo + 1\n            end\n            ngx.say(\"_G.foo: \", _G.foo)\n        ';\n    }\n--- request\n    GET /t\n--- response_body\n_G.foo: 1\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 18: github #257: globals cleared when code cache off\n--- http_config\n    lua_code_cache off;\n    init_by_lua '\n      test = setfenv(\n        function()\n          ngx.say(tostring(table))\n        end,\n        setmetatable({},\n        {\n          __index = function(self, key)\n          return rawget(self, key) or _G[key]\n        end\n      }))';\n--- config\n    location = /t {\n        content_by_lua 'test()';\n    }\n--- request\nGET /t\n--- response_body_like chop\n^table: 0x\\d*?[1-9a-fA-F]\n--- no_error_log\n[error]\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 19: lua_code_cache off + FFI-based Lua modules\n--- http_config\n    lua_code_cache off;\n    lua_package_path \"$prefix/html/?.lua;;\";\n\n--- config\n    location = /t {\n        content_by_lua '\n            if not jit then\n                ngx.say(\"skipped for non-LuaJIT\")\n            else\n                local test = require(\"test\")\n                ngx.say(\"test module loaded: \", test and true or false)\n                collectgarbage()\n            end\n        ';\n    }\n--- user_files\n>>> test.lua\nlocal ffi = require \"ffi\"\n\nffi.cdef[[\n    int my_test_function_here(void *p);\n    int my_test_function_here2(void *p);\n    int my_test_function_here3(void *p);\n]]\n\nreturn {\n}\n--- request\nGET /t\n--- response_body_like chop\n^(?:skipped for non-LuaJIT|test module loaded: true)$\n--- no_error_log\n[error]\n--- error_log eval\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n\n\n\n=== TEST 20: ngx.timer.* + ndk\n--- config\n    lua_code_cache off;\n    location /read {\n        echo ok;\n        log_by_lua '\n            ngx.timer.at(0, function ()\n                local foo = ndk.set_var.set_unescape_uri(\"a%20b\")\n                ngx.log(ngx.WARN, \"foo = \", foo)\n            end)\n        ';\n    }\n--- request\nGET /read\n--- response_body\nok\n--- wait: 0.1\n--- no_error_log\n[error]\n--- error_log eval\n[\"foo = a b\",\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/\n]\n\n\n\n=== TEST 21: set ngx.ctx before internal redirects performed by other nginx modules (with log_by_lua)\n--- config\n    lua_code_cache off;\n    location = /t {\n        rewrite_by_lua '\n            ngx.ctx.foo = \"hello world\";\n        ';\n        echo_exec /foo;\n    }\n\n    location = /foo {\n        echo hello;\n        log_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nhello\n--- no_error_log\n[error]\n--- log_level: debug\n--- error_log eval\n[\"lua release ngx.ctx at ref\",\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/,\n\"lua close the global Lua VM\",\n]\n\n\n\n=== TEST 22: set by lua file\n--- config\n    lua_code_cache off;\n    location /lua {\n        set_by_lua_file $res html/a.lua $arg_a $arg_b;\n        echo $res;\n    }\n--- user_files\n>>> a.lua\nreturn ngx.arg[1] + ngx.arg[2]\n--- request\nGET /lua?a=5&b=2\n--- response_body\n7\n--- no_error_log\n[error]\n--- error_log eval\n[qr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/,\n\"lua close the global Lua VM\",\n]\n\n\n\n=== TEST 23: simple set by lua\n--- config\n    lua_code_cache off;\n    location /lua {\n        set_by_lua $res \"return 1+1\";\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body\n2\n--- no_error_log\n[error]\n--- error_log eval\n[\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/,\n\"lua close the global Lua VM\",\n]\n\n\n\n=== TEST 24: lua_max_pending_timers - chained timers (non-zero delay) - not exceeding\n--- http_config\n    lua_max_pending_timers 1;\n    lua_code_cache off;\n\n--- config\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local function g()\n                s = s .. \"[g]\"\n                print(\"trace: \", s)\n            end\n\n            local function f()\n                local ok, err = ngx.timer.at(0.01, g)\n                if not ok then\n                    fail(\"failed to set timer: \", err)\n                    return\n                end\n                s = s .. \"[f]\"\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\ndecrementing the reference count for Lua VM: 3\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"trace: [m][f][g]\",\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/,\n\"lua close the global Lua VM\",\n\"decrementing the reference count for Lua VM: 2\",\n\"decrementing the reference count for Lua VM: 1\",\n]\n--- skip_eval: 11:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n\n\n\n=== TEST 25: lua variable sharing via upvalue\n--- http_config\n    lua_code_cache off;\n--- config\n    location /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local foo\n            local function f()\n                foo = 3\n                print(\"elapsed: \", ngx.now() - begin)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.06)\n            ngx.say(\"foo = \", foo)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nregistered timer\nfoo = 3\n\n--- wait: 0.1\n--- no_error_log\n[error]\ndecrementing the reference count for Lua VM: 3\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/,\n\"lua close the global Lua VM\",\n\"decrementing the reference count for Lua VM: 2\",\n\"decrementing the reference count for Lua VM: 1\",\n]\n\n\n\n=== TEST 26: lua_max_running_timers (just not enough)\n--- http_config\n    lua_max_running_timers 1;\n--- config\n    lua_code_cache off;\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local f, g\n\n            g = function ()\n                ngx.sleep(0.01)\n                collectgarbage()\n            end\n\n            f = function ()\n                ngx.sleep(0.01)\n                collectgarbage()\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.say(\"failed to set timer f: \", err)\n                return\n            end\n            local ok, err = ngx.timer.at(0, g)\n            if not ok then\n                ngx.say(\"failed to set timer g: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n\n--- error_log eval\n[\n\"1 lua_max_running_timers are not enough\",\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/,\n\"decrementing the reference count for Lua VM: 3\",\n\"decrementing the reference count for Lua VM: 2\",\n\"decrementing the reference count for Lua VM: 1\",\n\"lua close the global Lua VM\",\n]\n\n\n\n=== TEST 27: GC issue with the on_abort thread object\ncurl: (52) Empty reply from server\n--- config\n    lua_code_cache off;\n    location = /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.on_abort(function () end)\n            collectgarbage()\n            ngx.sleep(1)\n        ';\n    }\n--- request\n    GET /t\n--- abort\n--- timeout: 0.2\n--- wait: 1\n--- ignore_response\n--- no_error_log\n[error]\ndecrementing the reference count for Lua VM: 2\ndecrementing the reference count for Lua VM: 3\n--- error_log eval\n[\"decrementing the reference count for Lua VM: 1\",\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/,\n\"lua close the global Lua VM\",\n]\n--- curl_error eval\nqr/curl: \\(\\d+\\) Empty reply from server|curl: \\(28\\) Operation timed out after \\d+ milliseconds with 0 bytes received/\n\n\n\n=== TEST 28: multiple parallel timers\n--- config\n    lua_code_cache off;\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local function g()\n                s = s .. \"[g]\"\n                print(\"trace: \", s)\n            end\n\n            local function f()\n                s = s .. \"[f]\"\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                fail(\"failed to set timer: \", err)\n                return\n            end\n            local ok, err = ngx.timer.at(0.01, g)\n            if not ok then\n                fail(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\ndecrementing the reference count for Lua VM: 4\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"trace: [m][f][g]\",\n\"decrementing the reference count for Lua VM: 3\",\n\"decrementing the reference count for Lua VM: 2\",\n\"decrementing the reference count for Lua VM: 1\",\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/,\n\"lua close the global Lua VM\",\n]\n\n\n\n=== TEST 29: cosocket connection pool timeout (after Lua VM destroys)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    lua_code_cache off;\n    location = /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go(port)\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive(10)\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- request\nGET /t\n--- response_body\nconnected: 1, reused: 0\nrequest sent: 11\nreceived: OK\n--- no_error_log\n[error]\nlua tcp socket keepalive max idle timeout\n\n--- error_log eval\n[\nqq{lua tcp socket keepalive create connection pool for key \"127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}\"},\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/,\nqr/\\blua tcp socket keepalive: free connection pool [0-9A-F]+ for \"127.0.0.1:/,\n]\n--- skip_eval: 7:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n\n\n\n=== TEST 30: cosocket connection pool timeout (before Lua VM destroys)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    lua_code_cache off;\n    location = /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go(port)\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive(1)\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\n    ngx.sleep(0.01)\nend\n--- request\nGET /t\n--- response_body\nconnected: 1, reused: 0\nrequest sent: 11\nreceived: OK\n--- no_error_log\n[error]\n--- error_log eval\n[\nqq{lua tcp socket keepalive create connection pool for key \"127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}\"},\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/,\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket keepalive max idle timeout\",\n]\n\n\n\n=== TEST 31: lua_max_running_timers (just not enough, also low lua_max_pending_timers)\n--- http_config\n    lua_max_running_timers 1;\n    lua_max_pending_timers 10;\n--- config\n    lua_code_cache off;\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local f, g\n\n            g = function ()\n                ngx.sleep(0.01)\n                collectgarbage()\n            end\n\n            f = function ()\n                ngx.sleep(0.01)\n                collectgarbage()\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.say(\"failed to set timer f: \", err)\n                return\n            end\n            local ok, err = ngx.timer.at(0, g)\n            if not ok then\n                ngx.say(\"failed to set timer g: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n\n--- error_log eval\n[\n\"1 lua_max_running_timers are not enough\",\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\nqr/\\[alert\\] \\S+ lua_code_cache is off; this will hurt performance/,\n\"decrementing the reference count for Lua VM: 3\",\n\"decrementing the reference count for Lua VM: 2\",\n\"decrementing the reference count for Lua VM: 1\",\n\"lua close the global Lua VM\",\n]\n\n\n\n=== TEST 32: make sure inline code keys are correct\nGitHub issue #1428\n--- config\ninclude ../html/a/proxy.conf;\ninclude ../html/b/proxy.conf;\ninclude ../html/c/proxy.conf;\n\nlocation /t {\n    echo_location /a/;\n    echo_location /b/;\n    echo_location /a/;\n    echo_location /c/;\n}\n\n--- user_files\n>>> a/proxy.conf\nlocation /a/ {\n    content_by_lua_block { ngx.say(\"/a/ is called\") }\n}\n\n>>> b/proxy.conf\nlocation /b/ {\n    content_by_lua_block { ngx.say(\"/b/ is called\") }\n}\n\n>>> c/proxy.conf\nlocation /c/ {\n    content_by_lua_block { ngx.say(\"/b/ is called\") }\n}\n\n--- request\nGET /t\n--- response_body\n/a/ is called\n/b/ is called\n/a/ is called\n/b/ is called\n--- grep_error_log eval: qr/code cache .*/\n--- grep_error_log_out eval\n[\n\"code cache lookup (key='content_by_lua_nhli_3c7137b8371d10bc148c8f8bb3042ee6', ref=-1)\ncode cache miss (key='content_by_lua_nhli_3c7137b8371d10bc148c8f8bb3042ee6', ref=-1)\ncode cache lookup (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=-1)\ncode cache miss (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=-1)\ncode cache lookup (key='content_by_lua_nhli_3c7137b8371d10bc148c8f8bb3042ee6', ref=1)\ncode cache hit (key='content_by_lua_nhli_3c7137b8371d10bc148c8f8bb3042ee6', ref=1)\ncode cache lookup (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=-1)\ncode cache setting ref (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=2)\ncode cache hit (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=2)\n\",\n\"code cache lookup (key='content_by_lua_nhli_3c7137b8371d10bc148c8f8bb3042ee6', ref=-1)\ncode cache miss (key='content_by_lua_nhli_3c7137b8371d10bc148c8f8bb3042ee6', ref=-1)\ncode cache lookup (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=-1)\ncode cache miss (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=-1)\ncode cache lookup (key='content_by_lua_nhli_3c7137b8371d10bc148c8f8bb3042ee6', ref=1)\ncode cache hit (key='content_by_lua_nhli_3c7137b8371d10bc148c8f8bb3042ee6', ref=1)\ncode cache lookup (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=-1)\ncode cache setting ref (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=2)\ncode cache hit (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=2)\ncode cache lookup (key='content_by_lua_nhli_3c7137b8371d10bc148c8f8bb3042ee6', ref=1)\ncode cache hit (key='content_by_lua_nhli_3c7137b8371d10bc148c8f8bb3042ee6', ref=1)\ncode cache lookup (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=2)\ncode cache hit (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=2)\ncode cache lookup (key='content_by_lua_nhli_3c7137b8371d10bc148c8f8bb3042ee6', ref=1)\ncode cache hit (key='content_by_lua_nhli_3c7137b8371d10bc148c8f8bb3042ee6', ref=1)\ncode cache lookup (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=2)\ncode cache hit (key='content_by_lua_nhli_1dfe09105792ef65c8d576cc486d5e04', ref=2)\n\"]\n--- log_level: debug\n--- no_error_log\n[error]\n\n\n\n=== TEST 33: make sure Lua code file keys are correct\nGitHub issue #1428\n--- config\ninclude ../html/a/proxy.conf;\ninclude ../html/b/proxy.conf;\ninclude ../html/c/proxy.conf;\n\nlocation /t {\n    echo_location /a/;\n    echo_location /b/;\n    echo_location /a/;\n    echo_location /c/;\n}\n\n--- user_files\n>>> a.lua\nngx.say(\"/a/ is called\")\n\n>>> b.lua\nngx.say(\"/b/ is called\")\n\n>>> c.lua\nngx.say(\"/b/ is called\")\n\n>>> a/proxy.conf\nlocation /a/ {\n    content_by_lua_file html/a.lua;\n}\n\n>>> b/proxy.conf\nlocation /b/ {\n    content_by_lua_file html/b.lua;\n}\n\n>>> c/proxy.conf\nlocation /c/ {\n    content_by_lua_file html/c.lua;\n}\n\n--- request\nGET /t\n--- response_body\n/a/ is called\n/b/ is called\n/a/ is called\n/b/ is called\n--- grep_error_log eval: qr/code cache .*/\n--- grep_error_log_out eval\n[\n\"code cache lookup (key='nhlf_48a9a7def61143c003a7de1644e026e4', ref=-1)\ncode cache miss (key='nhlf_48a9a7def61143c003a7de1644e026e4', ref=-1)\ncode cache lookup (key='nhlf_68f5f4e946c3efd1cc206452b807e8b6', ref=-1)\ncode cache miss (key='nhlf_68f5f4e946c3efd1cc206452b807e8b6', ref=-1)\ncode cache lookup (key='nhlf_48a9a7def61143c003a7de1644e026e4', ref=1)\ncode cache hit (key='nhlf_48a9a7def61143c003a7de1644e026e4', ref=1)\ncode cache lookup (key='nhlf_042c9b3a136fbacbbd0e4b9ad10896b7', ref=-1)\ncode cache miss (key='nhlf_042c9b3a136fbacbbd0e4b9ad10896b7', ref=-1)\n\",\n\"code cache lookup (key='nhlf_48a9a7def61143c003a7de1644e026e4', ref=-1)\ncode cache miss (key='nhlf_48a9a7def61143c003a7de1644e026e4', ref=-1)\ncode cache lookup (key='nhlf_68f5f4e946c3efd1cc206452b807e8b6', ref=-1)\ncode cache miss (key='nhlf_68f5f4e946c3efd1cc206452b807e8b6', ref=-1)\ncode cache lookup (key='nhlf_48a9a7def61143c003a7de1644e026e4', ref=1)\ncode cache hit (key='nhlf_48a9a7def61143c003a7de1644e026e4', ref=1)\ncode cache lookup (key='nhlf_042c9b3a136fbacbbd0e4b9ad10896b7', ref=-1)\ncode cache miss (key='nhlf_042c9b3a136fbacbbd0e4b9ad10896b7', ref=-1)\ncode cache lookup (key='nhlf_48a9a7def61143c003a7de1644e026e4', ref=1)\ncode cache hit (key='nhlf_48a9a7def61143c003a7de1644e026e4', ref=1)\ncode cache lookup (key='nhlf_68f5f4e946c3efd1cc206452b807e8b6', ref=2)\ncode cache hit (key='nhlf_68f5f4e946c3efd1cc206452b807e8b6', ref=2)\ncode cache lookup (key='nhlf_48a9a7def61143c003a7de1644e026e4', ref=1)\ncode cache hit (key='nhlf_48a9a7def61143c003a7de1644e026e4', ref=1)\ncode cache lookup (key='nhlf_042c9b3a136fbacbbd0e4b9ad10896b7', ref=3)\ncode cache hit (key='nhlf_042c9b3a136fbacbbd0e4b9ad10896b7', ref=3)\n\"\n]\n--- log_level: debug\n--- no_error_log\n[error]\n\n\n\n=== TEST 34: variables in set_by_lua_file's file path\n--- config\n    location ~ ^/lua/(.+)$ {\n        set_by_lua_file $res html/$1.lua;\n        echo $res;\n    }\n\n    location /main {\n        echo_location /lua/a;\n        echo_location /lua/b;\n        echo_location /lua/a;\n        echo_location /lua/a;\n        echo_location /lua/b;\n    }\n--- user_files\n>>> a.lua\nreturn \"a\"\n>>> b.lua\nreturn \"b\"\n--- request\nGET /main\n--- response_body\na\nb\na\na\nb\n--- no_error_log\n[error]\n\n\n\n=== TEST 35: variables in rewrite_by_lua_file's file path\n--- config\n    location ~ ^/lua/(.+)$ {\n        rewrite_by_lua_file html/$1.lua;\n    }\n\n    location /main {\n        echo_location /lua/a;\n        echo_location /lua/b;\n        echo_location /lua/a;\n        echo_location /lua/a;\n        echo_location /lua/b;\n    }\n--- user_files\n>>> a.lua\nngx.say(\"a\")\n>>> b.lua\nngx.say(\"b\")\n--- request\nGET /main\n--- response_body\na\nb\na\na\nb\n--- no_error_log\n[error]\n\n\n\n=== TEST 36: variables in access_by_lua_file's file path\n--- config\n    location ~ ^/lua/(.+)$ {\n        access_by_lua_file html/$1.lua;\n\n        content_by_lua_block {\n            return\n        }\n    }\n\n    location ~ ^/proxy/(.+)$ {\n        proxy_pass http://127.0.0.1:$server_port/lua/$1;\n    }\n\n    location /main {\n        content_by_lua_block {\n            local res1, res2, res3, res4, res5 = ngx.location.capture_multi{\n                { \"/proxy/a\" },\n                { \"/proxy/b\" },\n                { \"/proxy/a\" },\n                { \"/proxy/a\" },\n                { \"/proxy/b\" },\n            }\n\n            ngx.say(res1.body)\n            ngx.say(res2.body)\n            ngx.say(res3.body)\n            ngx.say(res4.body)\n            ngx.say(res5.body)\n        }\n    }\n--- user_files\n>>> a.lua\nngx.print(\"a\")\n>>> b.lua\nngx.print(\"b\")\n--- request\nGET /main\n--- response_body\na\nb\na\na\nb\n--- no_error_log\n[error]\n\n\n\n=== TEST 37: variables in content_by_lua_file's file path\n--- config\n    location ~ ^/lua/(.+)$ {\n        content_by_lua_file html/$1.lua;\n    }\n\n    location /main {\n        echo_location /lua/a;\n        echo_location /lua/b;\n        echo_location /lua/a;\n        echo_location /lua/a;\n        echo_location /lua/b;\n    }\n--- user_files\n>>> a.lua\nngx.say(\"a\")\n>>> b.lua\nngx.say(\"b\")\n--- request\nGET /main\n--- response_body\na\nb\na\na\nb\n--- no_error_log\n[error]\n\n\n\n=== TEST 38: variables in header_filter_by_lua_file's file path\n--- config\n    location ~ ^/lua/(.+)$ {\n        return 200;\n\n        header_filter_by_lua_file html/$1.lua;\n    }\n\n    location ~ ^/proxy/(.+)$ {\n        proxy_pass http://127.0.0.1:$server_port/lua/$1;\n    }\n\n    location /main {\n        content_by_lua_block {\n            local res1, res2, res3, res4, res5 = ngx.location.capture_multi{\n                { \"/proxy/a\" },\n                { \"/proxy/b\" },\n                { \"/proxy/a\" },\n                { \"/proxy/a\" },\n                { \"/proxy/b\" },\n            }\n\n            ngx.say(res1.header.match)\n            ngx.say(res2.header.match)\n            ngx.say(res3.header.match)\n            ngx.say(res4.header.match)\n            ngx.say(res5.header.match)\n        }\n    }\n--- user_files\n>>> a.lua\nngx.header.match = \"a\"\n>>> b.lua\nngx.header.match = \"b\"\n--- request\nGET /main\n--- response_body\na\nb\na\na\nb\n--- no_error_log\n[error]\n\n\n\n=== TEST 39: variables in body_filter_by_lua_file's file path\n--- config\n    location ~ ^/lua/(.+)$ {\n        echo hello;\n\n        body_filter_by_lua_file html/$1.lua;\n    }\n\n    location /main {\n        echo_location /lua/a;\n        echo_location /lua/b;\n        echo_location /lua/a;\n        echo_location /lua/a;\n        echo_location /lua/b;\n    }\n--- user_files\n>>> a.lua\nngx.arg[1] = \"a\\n\"\nngx.arg[2] = true\n>>> b.lua\nngx.arg[1] = \"b\\n\"\nngx.arg[2] = true\n--- request\nGET /main\n--- response_body\na\nb\na\na\nb\n--- no_error_log\n[error]\n\n\n\n=== TEST 40: variables in log_by_lua_file's file path\n--- config\n    log_subrequest on;\n\n    location ~ ^/lua/(.+)$ {\n        echo hello;\n\n        log_by_lua_file html/$1.lua;\n    }\n\n    location /main {\n        echo_location /lua/a;\n        echo_location /lua/b;\n        echo_location /lua/a;\n        echo_location /lua/a;\n        echo_location /lua/b;\n    }\n--- user_files\n>>> a.lua\nngx.log(ngx.NOTICE, \"grep me: a\")\n>>> b.lua\nngx.log(ngx.NOTICE, \"grep me: b\")\n--- request\nGET /main\n--- ignore_response_body\n--- grep_error_log eval: qr/grep me: ([ab])/\n--- grep_error_log_out eval\n[\n\"grep me: a\ngrep me: b\ngrep me: a\ngrep me: a\ngrep me: b\n\",\n\"grep me: a\ngrep me: b\ngrep me: a\ngrep me: a\ngrep me: b\ngrep me: a\ngrep me: b\ngrep me: a\ngrep me: a\ngrep me: b\n\"]\n--- no_error_log\n[error]\n\n\n\n=== TEST 41: same chunk from different directives produces different closures\n--- http_config\n    ssl_session_fetch_by_lua_block { ngx.log(ngx.INFO, \"hello\") }\n\n    ssl_session_store_by_lua_block { ngx.log(ngx.INFO, \"hello\") }\n\n    upstream backend {\n        server unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        balancer_by_lua_block { ngx.log(ngx.INFO, \"hello\") }\n    }\n\n    server {\n        server_name test.com;\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        ssl_certificate_by_lua_block { ngx.log(ngx.INFO, \"hello\") }\n\n        location /lua {\n            set_by_lua_block $res { ngx.log(ngx.INFO, \"hello\") }\n\n            rewrite_by_lua_block { ngx.log(ngx.INFO, \"hello\") }\n\n            access_by_lua_block { ngx.log(ngx.INFO, \"hello\") }\n\n            content_by_lua_block { ngx.log(ngx.INFO, \"hello\") }\n\n            header_filter_by_lua_block { ngx.log(ngx.INFO, \"hello\") }\n\n            body_filter_by_lua_block { ngx.log(ngx.INFO, \"hello\") }\n\n            log_by_lua_block { ngx.log(ngx.INFO, \"hello\") }\n        }\n    }\n--- config\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location = /proxy {\n        proxy_pass http://backend;\n    }\n\n    location = /t {\n        set $html_dir $TEST_NGINX_HTML_DIR;\n\n        content_by_lua_block {\n            ngx.location.capture(\"/proxy\")\n\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            local ok, err = sock:connect(\"unix:\" .. ngx.var.html_dir .. \"/nginx.sock\")\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to connect: \", err)\n                return\n            end\n\n            local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n            if not sess then\n                ngx.log(ngx.ERR, \"failed to do SSL handshake: \", err)\n                return\n            end\n            package.loaded.session = sess\n            sock:close()\n\n            local ok, err = sock:connect(\"unix:\" .. ngx.var.html_dir .. \"/nginx.sock\")\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to connect: \", err)\n                return\n            end\n\n            local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n            if not sess then\n                ngx.log(ngx.ERR, \"failed to do SSL handshake: \", err)\n                return\n            end\n\n            local req = \"GET /lua HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.log(ngx.ERR, \"failed to send http request: \", err)\n                return\n            end\n        }\n    }\n--- request\nGET /t\n--- ignore_response_body\n--- grep_error_log eval: qr/code cache .*/\n--- grep_error_log_out eval\n[\n\"code cache lookup (key='content_by_lua_nhli_56ca4388611109b6ecfdeada050c8024', ref=-1)\ncode cache miss (key='content_by_lua_nhli_56ca4388611109b6ecfdeada050c8024', ref=-1)\ncode cache lookup (key='balancer_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='balancer_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='ssl_certificate_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='ssl_certificate_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='ssl_session_store_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='ssl_session_store_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='ssl_session_fetch_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='ssl_session_fetch_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='ssl_certificate_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=3)\ncode cache hit (key='ssl_certificate_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=3)\ncode cache lookup (key='ssl_session_store_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=4)\ncode cache hit (key='ssl_session_store_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=4)\ncode cache lookup (key='set_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='set_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='rewrite_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='rewrite_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='access_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='access_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='content_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='content_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='header_filter_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='header_filter_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='body_filter_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='body_filter_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='log_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='log_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\n\",\n\"code cache lookup (key='content_by_lua_nhli_56ca4388611109b6ecfdeada050c8024', ref=-1)\ncode cache miss (key='content_by_lua_nhli_56ca4388611109b6ecfdeada050c8024', ref=-1)\ncode cache lookup (key='balancer_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='balancer_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='ssl_certificate_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='ssl_certificate_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='ssl_session_store_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='ssl_session_store_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='ssl_session_fetch_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='ssl_session_fetch_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='ssl_certificate_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=3)\ncode cache hit (key='ssl_certificate_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=3)\ncode cache lookup (key='ssl_session_store_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=4)\ncode cache hit (key='ssl_session_store_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=4)\ncode cache lookup (key='set_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='set_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='rewrite_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='rewrite_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='access_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='access_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='content_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='content_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='header_filter_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='header_filter_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='body_filter_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='body_filter_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='log_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache miss (key='log_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=-1)\ncode cache lookup (key='content_by_lua_nhli_56ca4388611109b6ecfdeada050c8024', ref=1)\ncode cache hit (key='content_by_lua_nhli_56ca4388611109b6ecfdeada050c8024', ref=1)\ncode cache lookup (key='balancer_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=2)\ncode cache hit (key='balancer_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=2)\ncode cache lookup (key='ssl_certificate_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=3)\ncode cache hit (key='ssl_certificate_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=3)\ncode cache lookup (key='ssl_session_store_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=4)\ncode cache hit (key='ssl_session_store_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=4)\ncode cache lookup (key='ssl_session_fetch_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=5)\ncode cache hit (key='ssl_session_fetch_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=5)\ncode cache lookup (key='ssl_certificate_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=3)\ncode cache hit (key='ssl_certificate_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=3)\ncode cache lookup (key='ssl_session_store_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=4)\ncode cache hit (key='ssl_session_store_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=4)\ncode cache lookup (key='set_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=6)\ncode cache hit (key='set_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=6)\ncode cache lookup (key='rewrite_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=7)\ncode cache hit (key='rewrite_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=7)\ncode cache lookup (key='access_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=8)\ncode cache hit (key='access_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=8)\ncode cache lookup (key='content_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=9)\ncode cache hit (key='content_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=9)\ncode cache lookup (key='header_filter_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=10)\ncode cache hit (key='header_filter_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=10)\ncode cache lookup (key='body_filter_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=11)\ncode cache hit (key='body_filter_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=11)\ncode cache lookup (key='log_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=12)\ncode cache hit (key='log_by_lua_nhli_8a9441d0a30531ba8bb34ab11c55cfc3', ref=12)\n\"]\n--- error_log eval\n[\nqr/balancer_by_lua\\(nginx\\.conf:\\d+\\):\\d+: hello/,\nqr/ssl_session_fetch_by_lua\\(nginx\\.conf:\\d+\\):\\d+: hello/,\nqr/ssl_certificate_by_lua\\(nginx\\.conf:\\d+\\):\\d+: hello/,\nqr/ssl_session_store_by_lua\\(nginx\\.conf:\\d+\\):\\d+: hello/,\nqr/set_by_lua\\(nginx\\.conf:\\d+\\):\\d+: hello/,\nqr/rewrite_by_lua\\(nginx\\.conf:\\d+\\):\\d+: hello/,\nqr/access_by_lua\\(nginx\\.conf:\\d+\\):\\d+: hello/,\nqr/content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: hello/,\nqr/header_filter_by_lua\\(nginx\\.conf:\\d+\\):\\d+: hello/,\nqr/body_filter_by_lua\\(nginx\\.conf:\\d+\\):\\d+: hello/,\nqr/log_by_lua\\(nginx.conf:\\d+\\):\\d+: hello/,\n]\n--- log_level: debug\n--- no_error_log\n[error]\n--- skip_eval: 14:$ENV{TEST_NGINX_USE_HTTP3}\n"
  },
  {
    "path": "t/026-mysql.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => blocks() * repeat_each() * 3;\n\n#$ENV{LUA_PATH} = $ENV{HOME} . '/work/JSON4Lua-0.9.30/json/?.lua';\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: when mysql query timed out, kill that query by Lua\n--- no_http2\n--- http_config\n    upstream backend {\n        drizzle_server 127.0.0.1:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n--- config\n    location = /mysql {\n        #internal;\n        drizzle_send_query_timeout 100ms;\n        #drizzle_send_query_timeout 1s;\n        drizzle_query $echo_request_body;\n        drizzle_pass backend;\n\n        #error_page 504 /ret/504;\n        rds_json on;\n        more_set_headers -s 504 \"X-Mysql-Tid: $drizzle_thread_id\";\n    }\n\n    location /lua {\n        content_by_lua '\n            local sql = \"select sleep(5)\"\n            local res = ngx.location.capture(\"/mysql\",\n                { method = ngx.HTTP_POST, body = sql })\n\n            ngx.say(\"status = \" .. res.status)\n\n            local tid = res.header[\"X-Mysql-Tid\"]\n            if tid == nil then\n                ngx.say(\"thread id = nil\")\n                return\n            end\n\n            tid = tonumber(tid)\n            ngx.say(\"thread id = \" .. tid)\n\n            res = ngx.location.capture(\"/mysql\",\n                { method = ngx.HTTP_POST,\n                  body = \"kill query \" .. tid })\n\n            ngx.say(\"kill status = \" .. res.status)\n            ngx.say(\"kill body = \" .. res.body)\n        ';\n    }\n--- request\n    GET /lua\n--- response_body_like\n^status = 504\nthread id = \\d+\nkill status = 200\nkill body = \\{\"errcode\":0\\}$\n--- error_log eval\nqr{upstream timed out \\(\\d+: Connection timed out\\) while sending query to drizzle upstream}\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 2: no error pages\n--- http_config\n    upstream backend {\n        drizzle_server 127.0.0.1:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=300 mode=single overflow=ignore;\n    }\n--- config\n    location @err { echo Hi; }\n    error_page 504 = @err;\n    location = /mysql {\n        #internal;\n        drizzle_send_query_timeout 100ms;\n        #drizzle_send_query_timeout 1s;\n        drizzle_query $echo_request_body;\n        drizzle_pass backend;\n\n        no_error_pages;\n\n        rds_json on;\n        more_set_headers -s 504 \"X-Mysql-Tid: $drizzle_thread_id\";\n    }\n\n    location /lua {\n        content_by_lua '\n            local sql = \"select sleep(3)\"\n            local res = ngx.location.capture(\"/mysql\",\n                { method = ngx.HTTP_POST, body = sql })\n\n            ngx.say(\"status = \" .. res.status)\n\n            local tid = res.header[\"X-Mysql-Tid\"]\n            if tid == nil then\n                ngx.say(\"thread id = nil\")\n                return\n            end\n\n            tid = tonumber(tid)\n            ngx.say(\"thread id = \" .. tid)\n\n            res = ngx.location.capture(\"/mysql\",\n                { method = ngx.HTTP_POST,\n                  body = \"kill query \" .. tid })\n\n            ngx.say(\"kill status = \" .. res.status)\n            ngx.say(\"kill body = \" .. res.body)\n        ';\n    }\n--- request\n    GET /lua\n--- response_body_like\n^status = 504\nthread id = \\d+\nkill status = 200\nkill body = \\{\"errcode\":0\\}$\n--- SKIP\n"
  },
  {
    "path": "t/027-multi-capture.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(10);\n\nplan tests => repeat_each() * (blocks() * 2 + 4);\n\n#$ENV{LUA_PATH} = $ENV{HOME} . '/work/JSON4Lua-0.9.30/json/?.lua';\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\n#log_level 'warn';\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /foo {\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n        ';\n    }\n    location /a {\n        echo -n a;\n    }\n    location /b {\n        echo -n b;\n    }\n--- request\n    GET /foo\n--- response_body\nres1.status = 200\nres1.body = a\nres2.status = 200\nres2.body = b\n\n\n\n=== TEST 2: 4 concurrent requests\n--- config\n    location /foo {\n        content_by_lua '\n            local res1, res2, res3, res4 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n                { \"/c\" },\n                { \"/d\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n\n            ngx.say(\"res3.status = \" .. res3.status)\n            ngx.say(\"res3.body = \" .. res3.body)\n\n            ngx.say(\"res4.status = \" .. res4.status)\n            ngx.say(\"res4.body = \" .. res4.body)\n        ';\n    }\n    location ~ '^/([a-d])$' {\n        echo -n $1;\n    }\n--- request\n    GET /foo\n--- response_body\nres1.status = 200\nres1.body = a\nres2.status = 200\nres2.body = b\nres3.status = 200\nres3.body = c\nres4.status = 200\nres4.body = d\n\n\n\n=== TEST 3: capture multi in series\n--- config\n    location /foo {\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n\n            res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            ngx.say(\"2 res1.status = \" .. res1.status)\n            ngx.say(\"2 res1.body = \" .. res1.body)\n            ngx.say(\"2 res2.status = \" .. res2.status)\n            ngx.say(\"2 res2.body = \" .. res2.body)\n\n        ';\n    }\n    location /a {\n        echo -n a;\n    }\n    location /b {\n        echo -n b;\n    }\n--- request\n    GET /foo\n--- response_body\nres1.status = 200\nres1.body = a\nres2.status = 200\nres2.body = b\n2 res1.status = 200\n2 res1.body = a\n2 res2.status = 200\n2 res2.body = b\n\n\n\n=== TEST 4: capture multi in subrequest\n--- config\n    location /foo {\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n\n            local n = ngx.var.arg_n\n\n            ngx.say(n .. \" res1.status = \" .. res1.status)\n            ngx.say(n .. \" res1.body = \" .. res1.body)\n            ngx.say(n .. \" res2.status = \" .. res2.status)\n            ngx.say(n .. \" res2.body = \" .. res2.body)\n        ';\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo?n=1\")\n            ngx.say(\"top res.status = \" .. res.status)\n            ngx.say(\"top res.body = [\" .. res.body .. \"]\")\n        ';\n    }\n\n    location /a {\n        echo -n a;\n    }\n\n    location /b {\n        echo -n b;\n    }\n--- request\n    GET /main\n--- response_body\ntop res.status = 200\ntop res.body = [1 res1.status = 200\n1 res1.body = a\n1 res2.status = 200\n1 res2.body = b\n]\n\n\n\n=== TEST 5: capture multi in parallel\n--- config\n    location ~ '^/(foo|bar)$' {\n        set $tag $1;\n        content_by_lua '\n            local res1, res2\n            if ngx.var.tag == \"foo\" then\n                res1, res2 = ngx.location.capture_multi{\n                    { \"/a\" },\n                    { \"/b\" },\n                }\n            else\n                res1, res2 = ngx.location.capture_multi{\n                    { \"/c\" },\n                    { \"/d\" },\n                }\n            end\n\n            local n = ngx.var.arg_n\n\n            ngx.say(n .. \" res1.status = \" .. res1.status)\n            ngx.say(n .. \" res1.body = \" .. res1.body)\n            ngx.say(n .. \" res2.status = \" .. res2.status)\n            ngx.say(n .. \" res2.body = \" .. res2.body)\n        ';\n    }\n\n    location /main {\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/foo?n=1\" },\n                { \"/bar?n=2\" },\n            }\n\n            ngx.say(\"top res1.status = \" .. res1.status)\n            ngx.say(\"top res1.body = [\" .. res1.body .. \"]\")\n            ngx.say(\"top res2.status = \" .. res2.status)\n            ngx.say(\"top res2.body = [\" .. res2.body .. \"]\")\n        ';\n    }\n\n    location ~ '^/([abcd])$' {\n        echo -n $1;\n    }\n--- request\n    GET /main\n--- response_body\ntop res1.status = 200\ntop res1.body = [1 res1.status = 200\n1 res1.body = a\n1 res2.status = 200\n1 res2.body = b\n]\ntop res2.status = 200\ntop res2.body = [2 res1.status = 200\n2 res1.body = c\n2 res2.status = 200\n2 res2.body = d\n]\n\n\n\n=== TEST 6: memc sanity\n--- config\n    location /foo {\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n        ';\n    }\n    location ~ '^/[ab]$' {\n        set $memc_key $uri;\n        set $memc_value hello;\n        set $memc_cmd set;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n--- request\n    GET /foo\n--- response_body eval\n\"res1.status = 201\nres1.body = STORED\\r\n\nres2.status = 201\nres2.body = STORED\\r\n\n\"\n\n\n\n=== TEST 7: memc muti + multi\n--- config\n    location /main {\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/foo?n=1\" },\n                { \"/bar?n=2\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = [\" .. res1.body .. \"]\")\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = [\" .. res2.body .. \"]\")\n        ';\n    }\n    location ~ '^/(foo|bar)$' {\n        set $tag $1;\n        content_by_lua '\n            local res1, res2\n            if ngx.var.tag == \"foo\" then\n                res1, res2 = ngx.location.capture_multi{\n                    { \"/a\" },\n                    { \"/b\" },\n                }\n            else\n                res1, res2 = ngx.location.capture_multi{\n                    { \"/c\" },\n                    { \"/d\" },\n                }\n            end\n            print(\"args: \" .. ngx.var.args)\n            local n = ngx.var.arg_n\n            ngx.say(n .. \" res1.status = \" .. res1.status)\n            ngx.say(n .. \" res1.body = \" .. res1.body)\n            ngx.say(n .. \" res2.status = \" .. res2.status)\n            ngx.say(n .. \" res2.body = \" .. res2.body)\n        ';\n    }\n    location ~ '^/[abcd]$' {\n        set $memc_key $uri;\n        set $memc_value hello;\n        set $memc_cmd set;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n--- request\n    GET /main\n--- response_body eval\n\"res1.status = 200\nres1.body = [1 res1.status = 201\n1 res1.body = STORED\\r\n\n1 res2.status = 201\n1 res2.body = STORED\\r\n\n]\nres2.status = 200\nres2.body = [2 res1.status = 201\n2 res1.body = STORED\\r\n\n2 res2.status = 201\n2 res2.body = STORED\\r\n\n]\n\"\n\n\n\n=== TEST 8: memc 4 concurrent requests\n--- config\n    location /foo {\n        content_by_lua '\n            local res1, res2, res3, res4 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n                { \"/c\" },\n                { \"/d\" },\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n\n            ngx.say(\"res3.status = \" .. res3.status)\n            ngx.say(\"res3.body = \" .. res3.body)\n\n            ngx.say(\"res4.status = \" .. res4.status)\n            ngx.say(\"res4.body = \" .. res4.body)\n        ';\n    }\n    location ~ '^/[a-d]$' {\n        set $memc_key $uri;\n        set $memc_value hello;\n        set $memc_cmd set;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n--- request\n    GET /foo\n--- response_body eval\n\"res1.status = 201\nres1.body = STORED\\r\n\nres2.status = 201\nres2.body = STORED\\r\n\nres3.status = 201\nres3.body = STORED\\r\n\nres4.status = 201\nres4.body = STORED\\r\n\n\"\n\n\n\n=== TEST 9: capture multi in series (more complex)\n--- config\n    location /foo {\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            local res3, res4 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            res3, res4 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n            ngx.say(\"res3.status = \" .. res3.status)\n            ngx.say(\"res3.body = \" .. res3.body)\n            ngx.say(\"res4.status = \" .. res4.status)\n            ngx.say(\"res4.body = \" .. res4.body)\n\n        ';\n    }\n    location /a {\n        echo -n a;\n    }\n    location /b {\n        echo -n b;\n    }\n    location /main {\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/foo\" },\n                { \"/foo\" },\n            }\n            local res3, res4 = ngx.location.capture_multi{\n                { \"/foo\" },\n                { \"/foo\" },\n            }\n            ngx.print(res1.body)\n            ngx.print(res2.body)\n            ngx.print(res3.body)\n            ngx.print(res4.body)\n        ';\n    }\n--- request\n    GET /main\n--- response_body eval\n\"res1.status = 200\nres1.body = a\nres2.status = 200\nres2.body = b\nres3.status = 200\nres3.body = a\nres4.status = 200\nres4.body = b\n\" x 4\n\n\n\n=== TEST 10: capture multi in series (more complex, using memc)\n--- config\n    location /foo {\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            res1, res2 = ngx.location.capture_multi{\n                { \"/a\" },\n                { \"/b\" },\n            }\n            local res3, res4 = ngx.location.capture_multi{\n                { \"/c\" },\n                { \"/d\" },\n            }\n            res3, res4 = ngx.location.capture_multi{\n                { \"/e\" },\n                { \"/f\" },\n            }\n\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n            ngx.say(\"res3.status = \" .. res3.status)\n            ngx.say(\"res3.body = \" .. res3.body)\n            ngx.say(\"res4.status = \" .. res4.status)\n            ngx.say(\"res4.body = \" .. res4.body)\n        ';\n    }\n\n    location /memc {\n        set $memc_key $arg_val;\n        set $memc_value $arg_val;\n        set $memc_cmd $arg_cmd;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location ~ '^/([a-f])$' {\n        set $tag $1;\n        content_by_lua '\n            ngx.location.capture(\"/memc?cmd=set&val=\" .. ngx.var.tag)\n            local res = ngx.location.capture(\"/memc?cmd=get&val=\" .. ngx.var.tag)\n            ngx.print(res.body)\n        ';\n    }\n\n    location /main {\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/foo\" },\n                { \"/foo\" },\n            }\n            local res3, res4 = ngx.location.capture_multi{\n                { \"/foo\" },\n                { \"/foo\" },\n            }\n            ngx.print(res1.body)\n            ngx.print(res2.body)\n            ngx.print(res3.body)\n            ngx.print(res4.body)\n        ';\n    }\n--- request\n    GET /main\n--- response_body2\n--- response_body eval\n\"res1.status = 200\nres1.body = a\nres2.status = 200\nres2.body = b\nres3.status = 200\nres3.body = e\nres4.status = 200\nres4.body = f\n\" x 4\n--- no_error_log eval\n[\"[error]\", \"[alert]\"]\n--- timeout: 10\n\n\n\n=== TEST 11: a mixture of rewrite, access, content phases\n--- config\n    location /main {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/a\")\n            print(\"rewrite a: \" .. res.body)\n\n            res = ngx.location.capture(\"/b\")\n            print(\"rewrite b: \" .. res.body)\n\n            res = ngx.location.capture(\"/c\")\n            print(\"rewrite c: \" .. res.body)\n        ';\n\n        access_by_lua '\n            local res = ngx.location.capture(\"/A\")\n            print(\"access A: \" .. res.body)\n\n            res = ngx.location.capture(\"/B\")\n            print(\"access B: \" .. res.body)\n        ';\n\n        content_by_lua '\n            local res = ngx.location.capture(\"/d\")\n            ngx.say(\"content d: \" .. res.body)\n\n            res = ngx.location.capture(\"/e\")\n            ngx.say(\"content e: \" .. res.body)\n\n            res = ngx.location.capture(\"/f\")\n            ngx.say(\"content f: \" .. res.body)\n        ';\n    }\n\n    location /memc {\n        set $memc_key $arg_val;\n        set $memc_value $arg_val;\n        set $memc_cmd $arg_cmd;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location ~ '^/([A-F])$' {\n        echo -n $1;\n    }\n\n    location ~ '^/([a-f])$' {\n        set $tag $1;\n        content_by_lua '\n            ngx.location.capture(\"/memc?cmd=set&val=\" .. ngx.var.tag)\n            local res = ngx.location.capture(\"/memc?cmd=get&val=\" .. ngx.var.tag)\n            ngx.print(res.body)\n        ';\n    }\n--- request\n    GET /main\n--- response_body\ncontent d: d\ncontent e: e\ncontent f: f\n\n--- log_level: info\n--- grep_error_log eval: qr/rewrite .+?(?= while )|access .+?(?=,)/\n--- grep_error_log_out\nrewrite a: a\nrewrite b: b\nrewrite c: c\naccess A: A\naccess B: B\n\n\n\n=== TEST 12: a mixture of rewrite, access, content phases\n--- config\n    location /main {\n        rewrite_by_lua '\n            local a, b, c = ngx.location.capture_multi{\n                {\"/a\"}, {\"/b\"}, {\"/c\"},\n            }\n            print(\"rewrite a: \" .. a.body)\n            print(\"rewrite b: \" .. b.body)\n            print(\"rewrite c: \" .. c.body)\n        ';\n\n        access_by_lua '\n            local A, B = ngx.location.capture_multi{\n                {\"/A\"}, {\"/B\"},\n            }\n            print(\"access A: \" .. A.body)\n            print(\"access B: \" .. B.body)\n        ';\n\n        content_by_lua '\n            local d, e, f = ngx.location.capture_multi{\n                {\"/d\"}, {\"/e\"}, {\"/f\"},\n            }\n            ngx.say(\"content d: \" .. d.body)\n            ngx.say(\"content e: \" .. e.body)\n            ngx.say(\"content f: \" .. f.body)\n        ';\n    }\n\n    location /memc {\n        set $memc_key $arg_val;\n        set $memc_value $arg_val;\n        set $memc_cmd $arg_cmd;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n\n    location ~ '^/([A-F])$' {\n        echo -n $1;\n    }\n\n    location ~ '^/([a-f])$' {\n        set $tag $1;\n        content_by_lua '\n            ngx.location.capture(\"/memc?cmd=set&val=\" .. ngx.var.tag)\n            local res = ngx.location.capture(\"/memc?cmd=get&val=\" .. ngx.var.tag)\n            ngx.print(res.body)\n        ';\n    }\n--- request\n    GET /main\n--- stap2\nglobal delta = \"  \"\n\nM(http-subrequest-start) {\n    r = $arg1\n    n = ngx_http_subreq_depth(r)\n    pr = ngx_http_req_parent(r)\n    printf(\"%sbegin %s -> %s (%d)\\n\", ngx_indent(n, delta),\n        ngx_http_req_uri(pr),\n        ngx_http_req_uri(r),\n        n)\n}\n\nF(ngx_http_lua_run_thread) {\n    r = $r\n    uri = ngx_http_req_uri(r)\n    if (uri == \"/main\") {\n        printf(\"run thread %s: %d\\n\", uri, $nret)\n        #print_ubacktrace()\n    }\n}\n\nM(http-lua-info) {\n    uri = ngx_http_req_uri($r)\n    #if (uri == \"/main\") {\n    printf(\"XXX info: %s: %s\", uri, user_string($arg1))\n    #}\n}\n\nF(ngx_http_lua_post_subrequest) {\n    r = $r\n    n = ngx_http_subreq_depth(r)\n    pr = ngx_http_req_parent(r)\n\n    printf(\"%send %s -> %s (%d)\\n\", ngx_indent(n, delta),\n        ngx_http_req_uri(r),\n        ngx_http_req_uri(pr),\n        n)\n}\n\nF(ngx_http_lua_handle_subreq_responses) {\n    r = $r\n    n = ngx_http_subreq_depth(r)\n    printf(\"%shandle res %s (%d)\\n\", ngx_indent(n, delta), ngx_http_req_uri(r), n)\n}\n\n--- response_body\ncontent d: d\ncontent e: e\ncontent f: f\n--- log_level: info\n--- grep_error_log eval: qr/rewrite .+?(?= while )|access .+?(?=,)/\n--- grep_error_log_out\nrewrite a: a\nrewrite b: b\nrewrite c: c\naccess A: A\naccess B: B\n\n\n\n=== TEST 13: proxy_cache_lock in subrequests\n--- http_config\nproxy_cache_lock on;\nproxy_cache_lock_timeout 100ms;\nproxy_connect_timeout 300ms;\n\nproxy_cache_path conf/cache levels=1:2 keys_zone=STATIC:10m inactive=10m max_size=1m;\n\n--- config\n    location /foo {\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/proxy\" },\n                { \"/proxy\" },\n                { \"/proxy\" },\n                { \"/proxy\" },\n            }\n            ngx.say(\"ok\")\n        ';\n    }\n\n    location = /proxy {\n            proxy_cache STATIC;\n            proxy_pass http://127.0.0.2:12345;\n            proxy_cache_key $proxy_host$uri$args;\n            proxy_cache_valid any 1s;\n            #proxy_http_version 1.1;\n    }\n--- request\n    GET /foo\n--- response_body\nok\n\n\n\n=== TEST 14: capture multi with headers\n--- config\n    location /foo {\n        content_by_lua_block {\n            local res1, res2, res3 = ngx.location.capture_multi{\n                {\"/test\", { headers = { [\"X-Test-Header\"] = \"aa\"} } },\n                {\"/test\", { headers = { [\"X-Test-Header\"] = \"bb\"} } },\n                {\"/test\"},\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n            ngx.say(\"res3.status = \" .. res3.status)\n            ngx.say(\"res3.body = \" .. res3.body)\n        }\n    }\n\n    location = /test {\n        content_by_lua_block {\n            ngx.print(ngx.var.http_x_test_header)\n        }\n    }\n--- request\n    GET /foo\n--- response_body\nres1.status = 200\nres1.body = aa\nres2.status = 200\nres2.body = bb\nres3.status = 200\nres3.body = nil\n\n\n\n=== TEST 15: capture multi with headers override\n--- config\n    location /foo {\n        content_by_lua_block {\n            local res1, res2, res3 = ngx.location.capture_multi{\n                {\"/test\", { headers = { [\"X-Test-Header\"] = \"aa\"} } },\n                {\"/test\", { headers = { [\"X-Test-Header\"] = \"bb\"} } },\n                {\"/test\"},\n            }\n            ngx.say(\"res1.status = \" .. res1.status)\n            ngx.say(\"res1.body = \" .. res1.body)\n            ngx.say(\"res2.status = \" .. res2.status)\n            ngx.say(\"res2.body = \" .. res2.body)\n            ngx.say(\"res3.status = \" .. res3.status)\n            ngx.say(\"res3.body = \" .. res3.body)\n        }\n    }\n\n    location = /test {\n        content_by_lua_block {\n            ngx.print(ngx.var.http_x_test_header)\n        }\n    }\n--- request\n    GET /foo\n--- more_headers\nX-Test-Header: cc\n--- response_body\nres1.status = 200\nres1.body = aa\nres2.status = 200\nres2.body = bb\nres3.status = 200\nres3.body = cc\n"
  },
  {
    "path": "t/028-req-header.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (2 * blocks() + 48);\n\n#no_diff();\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: random access req headers\n--- config\n    location /req-header {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(\"Foo: \", headers[\"Foo\"] or \"nil\")\n            ngx.say(\"Bar: \", headers[\"Bar\"] or \"nil\")\n        ';\n    }\n--- request\nGET /req-header\n--- more_headers\nFoo: bar\nBar: baz\n--- response_body\nFoo: bar\nBar: baz\n--- log_level: debug\n--- no_error_log\nlua exceeding request header limit\n\n\n\n=== TEST 2: iterating through headers\n--- config\n    location /req-header {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers(nil, true)\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            local h = {}\n            for k, v in pairs(headers) do\n                h[k] = v\n            end\n            if (ngx.req.http_version() == 3 or ngx.req.http_version() == 2) then\n                ngx.say(\"Foo: \", h[\"foo\"] or \"nil\")\n                ngx.say(\"Bar: \", h[\"bar\"] or \"nil\")\n            else\n                ngx.say(\"Foo: \", h[\"Foo\"] or \"nil\")\n                ngx.say(\"Bar: \", h[\"Bar\"] or \"nil\")\n            end \n        ';\n    }\n--- request\nGET /req-header\n--- more_headers\nFoo: bar\nBar: baz\n--- response_body\nFoo: bar\nBar: baz\n\n\n\n=== TEST 3: set input header\n--- config\n    location /req-header {\n        rewrite_by_lua '\n            ngx.req.set_header(\"Foo\", \"new value\");\n        ';\n\n        echo \"Foo: $http_foo\";\n    }\n--- request\nGET /req-header\n--- more_headers\nFoo: bar\nBar: baz\n--- response_body\nFoo: new value\n\n\n\n=== TEST 4: clear input header\n--- config\n    location /req-header {\n        rewrite_by_lua '\n            ngx.req.set_header(\"Foo\", nil);\n        ';\n\n        echo \"Foo: $http_foo\";\n    }\n--- request\nGET /req-header\n--- more_headers\nFoo: bar\nBar: baz\n--- response_body\nFoo: \n\n\n\n=== TEST 5: rewrite content length\n--- config\n    location /bar {\n        rewrite_by_lua '\n            ngx.req.set_header(\"Content-Length\", 2048)\n        ';\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request eval\n\"POST /bar\\n\" .\n\"a\" x 4096\n--- response_body eval\n\"a\" x 2048\n--- timeout: 15\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3}\n--- no_http2\n\n\n\n=== TEST 6: rewrite content length (normalized form)\n--- config\n    location /bar {\n        rewrite_by_lua '\n            ngx.req.set_header(\"content-length\", 2048)\n        ';\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request eval\n\"POST /bar\\n\" .\n\"a\" x 4096\n--- response_body eval\n\"a\" x 2048\n--- timeout: 15\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3}\n--- no_http2\n\n\n\n=== TEST 7: rewrite host and user-agent\n--- config\n    location /bar {\n        rewrite_by_lua '\n            ngx.req.set_header(\"Host\", \"foo\")\n            ngx.req.set_header(\"User-Agent\", \"blah\")\n        ';\n        echo \"Host: $host\";\n        echo \"User-Agent: $http_user_agent\";\n    }\n--- request\nGET /bar\n--- response_body\nHost: foo\nUser-Agent: blah\n\n\n\n=== TEST 8: clear host and user-agent\n$host always has a default value and cannot be really cleared.\n--- config\n    location /bar {\n        rewrite_by_lua '\n            ngx.req.set_header(\"Host\", nil)\n            ngx.req.set_header(\"User-Agent\", nil)\n        ';\n        echo \"Host: $host\";\n        echo \"Host (2): $http_host\";\n        echo \"User-Agent: $http_user_agent\";\n    }\n--- request\nGET /bar\n--- response_body\nHost: localhost\nHost (2): \nUser-Agent: \n\n\n\n=== TEST 9: clear host and user-agent (the other way)\n--- config\n    location /bar {\n        rewrite_by_lua '\n            ngx.req.clear_header(\"Host\")\n            ngx.req.clear_header(\"User-Agent\")\n            ngx.req.clear_header(\"X-Foo\")\n        ';\n        echo \"Host: $host\";\n        echo \"User-Agent: $http_user_agent\";\n        echo \"X-Foo: $http_x_foo\";\n    }\n--- request\nGET /bar\n--- more_headers\nX-Foo: bar\n--- response_body\nHost: localhost\nUser-Agent: \nX-Foo: \n\n\n\n=== TEST 10: clear content-length\n--- config\n    location /bar {\n        access_by_lua '\n            ngx.req.clear_header(\"Content-Length\")\n        ';\n        echo \"Content-Length: $http_content_length\";\n    }\n--- request\nPOST /bar\nhello\n--- more_headers\n--- response_body\nContent-Length: \n\n\n\n=== TEST 11: rewrite type\n--- config\n    location /bar {\n        access_by_lua '\n            ngx.req.set_header(\"Content-Type\", \"text/css\")\n        ';\n        echo \"Content-Type: $content_type\";\n    }\n--- request\nPOST /bar\nhello\n--- more_headers\nContent-Type: text/plain\n--- response_body\nContent-Type: text/css\n\n\n\n=== TEST 12: clear type\n--- config\n    location /bar {\n        access_by_lua '\n            ngx.req.clear_header(\"Content-Type\")\n        ';\n        echo \"Content-Type: $content_type\";\n    }\n--- request\nPOST /bar\nhello\n--- more_headers\nContent-Type: text/plain\n--- response_body\nContent-Type: \n\n\n\n=== TEST 13: add multiple request headers\n--- config\n    location /bar {\n        access_by_lua '\n            ngx.req.set_header(\"Foo\", {\"a\", \"b\"})\n        ';\n        echo \"Foo: $http_foo\";\n    }\n--- request\nGET /bar\n--- response_body eval\n# Since nginx version 1.23.0, nginx combines same $http_* variable together\n$Test::Nginx::Util::NginxVersion >= 1.023000 ?\n\n\"Foo: a, b\\n\"\n:\n\"Foo: a\\n\"\n\n\n\n=== TEST 14: add multiple request headers\n--- config\n    location /bar {\n        access_by_lua '\n            ngx.req.set_header(\"Foo\", {\"a\", \"abc\"})\n        ';\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n\n    location = /foo {\n        echo $echo_client_request_headers;\n    }\n--- request\nGET /bar\n--- response_body_like chomp\n\\bFoo: a\\r\\n.*?\\bFoo: abc\\b\n\n\n\n=== TEST 15: set_header and clear_header should refresh ngx.req.get_headers() automatically\n--- config\n    location /foo {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(\"Foo: \", headers[\"Foo\"] or \"nil\")\n\n            ngx.req.set_header(\"Foo\", 32)\n\n            headers, err = ngx.req.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(\"Foo 1: \", headers[\"Foo\"] or \"nil\")\n\n            ngx.req.set_header(\"Foo\", \"abc\")\n\n            headers, err = ngx.req.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(\"Foo 2: \", headers[\"Foo\"] or \"nil\")\n\n            ngx.req.clear_header(\"Foo\")\n\n            headers, err = ngx.req.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(\"Foo 3: \", headers[\"Foo\"] or \"nil\")\n        ';\n    }\n--- more_headers\nFoo: foo\n\n--- request\n    GET /foo\n--- response_body\nFoo: foo\nFoo 1: 32\nFoo 2: abc\nFoo 3: nil\n\n\n\n=== TEST 16: duplicate req headers\n--- config\n    location /foo {\n        content_by_lua '\n            collectgarbage()\n            local headers, err = ngx.req.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            local vals = headers[\"Foo\"]\n            ngx.say(\"value is of type \", type(vals), \".\")\n            if type(vals) == \"table\" then\n                ngx.say(\"Foo takes \", #vals or \"nil\", \" values.\")\n                ngx.say(\"They are \", table.concat(vals, \", \"), \".\")\n            end\n        ';\n    }\n--- more_headers\nFoo: foo\nFoo: bar\nFoo: baz\n--- request\n    GET /foo\n--- response_body\nvalue is of type table.\nFoo takes 3 values.\nThey are foo, bar, baz.\n\n\n\n=== TEST 17: Accept-Encoding (scalar)\n--- config\n    location /bar {\n        default_type 'text/plain';\n        rewrite_by_lua '\n            ngx.req.set_header(\"Accept-Encoding\", \"gzip\")\n        ';\n        gzip on;\n        gzip_min_length  1;\n        gzip_buffers     4 8k;\n        gzip_types       text/plain;\n    }\n--- user_files\n\">>> bar\n\" . (\"hello\" x 512)\n--- request\nGET /bar\n--- response_headers\nContent-Encoding: gzip\n--- response_body_like: .{20}\n\n\n\n=== TEST 18: Accept-Encoding (table)\n--- config\n    location /bar {\n        default_type 'text/plain';\n        rewrite_by_lua '\n            ngx.req.set_header(\"Accept-Encoding\", {\"gzip\"})\n        ';\n        gzip on;\n        gzip_min_length  1;\n        gzip_buffers     4 8k;\n        gzip_types       text/plain;\n    }\n--- user_files\n\">>> bar\n\" . (\"hello\" x 512)\n--- request\nGET /bar\n--- response_headers\nContent-Encoding: gzip\n--- response_body_like: .{20}\n\n\n\n=== TEST 19: exceeding default max 100 header limit\n--- config\n    location /lua {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers()\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(headers) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \": \", headers[key])\n            end\n        ';\n    }\n--- request\nGET /lua\n--- more_headers eval\nmy $i = 1;\nmy $s;\nwhile ($i <= 99) {\n    $s .= \"X-$i:$i\\n\";\n    $i++;\n}\n$s\n--- response_body eval\nmy @k;\nmy $i = 1;\nwhile ($i <= 98) {\n    push @k, \"x-$i\";\n    $i++;\n}\npush @k, \"connection: close\\n\";\npush @k, \"host: localhost\\n\";\n@k = sort @k;\nfor my $k (@k) {\n    if ($k =~ /\\d+/) {\n        $k .= \": $&\\n\";\n    }\n}\n\"err: truncated\\n\" . CORE::join(\"\", @k);\n--- timeout: 4\n--- error_log\nlua exceeding request header limit 101 > 100\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n--- no_http2\n\n\n\n=== TEST 20: NOT exceeding default max 100 header limit\n--- config\n    location /lua {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers()\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(headers) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            local cnt = 0\n            for i, key in ipairs(keys) do\n                ngx.say(key, \": \", headers[key])\n                cnt = cnt + 1\n            end\n            ngx.say(\"found \", cnt, \" headers\")\n        ';\n    }\n--- request\nGET /lua\n--- more_headers eval\nmy $i = 1;\nmy $s;\nwhile ($i <= 98) {\n    $s .= \"X-$i:$i\\n\";\n    $i++;\n}\n$s\n--- response_body eval\nmy @k;\n\nif (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    push @k, \"host: localhost\\n\";\n}\nmy $i = 1;\nwhile ($i <= 98) {\n    push @k, \"x-$i\";\n    $i++;\n}\n\nmy $found_headers = \"found 99 headers\\n\";\nif (!defined($ENV{TEST_NGINX_USE_HTTP3}) && !defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    push @k, \"connection: close\\n\";\n    push @k, \"host: localhost\\n\";\n    $found_headers = \"found 100 headers\\n\";\n}\n@k = sort @k;\nfor my $k (@k) {\n    if ($k =~ /\\d+/) {\n        $k .= \": $&\\n\";\n    }\n}\n\nCORE::join(\"\", @k) . $found_headers;\n--- timeout: 4\n--- no_error_log\n[error]\nlua exceeding request header limit\n\n\n\n=== TEST 21: exceeding custom max 102 header limit\n--- config\n    location /lua {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers(102)\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(headers) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \": \", headers[key])\n            end\n        ';\n    }\n--- request\nGET /lua\n--- more_headers eval\nmy $i = 1;\nmy $s;\nwhile ($i <= 101) {\n    $s .= \"X-$i:$i\\n\";\n    $i++;\n}\n$s\n--- response_body eval\nmy @k;\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    push @k, \"host: localhost\\n\";\n}\nmy $i = 1;\nwhile ($i <= 100) {\n    push @k, \"x-$i\";\n    $i++;\n}\npush @k, \"connection: close\\n\";\npush @k, \"host: localhost\\n\";\n@k = sort @k;\nfor my $k (@k) {\n    if ($k =~ /\\d+/) {\n        $k .= \": $&\\n\";\n    }\n}\n\"err: truncated\\n\" . CORE::join(\"\", @k);\n--- timeout: 4\n--- error_log\nlua exceeding request header limit 103 > 102\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n--- no_http2\n\n\n\n=== TEST 22: NOT exceeding custom max 102 header limit\n--- config\n    location /lua {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers(102)\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(headers) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \": \", headers[key])\n            end\n        ';\n    }\n--- request\nGET /lua\n--- more_headers eval\nmy $i = 1;\nmy $s;\nwhile ($i <= 100) {\n    $s .= \"X-$i:$i\\n\";\n    $i++;\n}\n$s\n--- response_body eval\nmy @k;\nif (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    push @k, \"host: localhost\\n\";\n}\nmy $i = 1;\nwhile ($i <= 100) {\n    push @k, \"x-$i\";\n    $i++;\n}\n\nif (!defined($ENV{TEST_NGINX_USE_HTTP3}) && !defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    push @k, \"connection: close\\n\";\n    push @k, \"host: localhost\\n\";\n}\n\n@k = sort @k;\nfor my $k (@k) {\n    if ($k =~ /\\d+/) {\n        $k .= \": $&\\n\";\n    }\n}\nCORE::join(\"\", @k);\n--- timeout: 4\n--- no_error_log\n[error]\nlua exceeding request header limit\n\n\n\n=== TEST 23: custom unlimited headers\n--- config\n    location /lua {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers(0)\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(headers) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \": \", headers[key])\n            end\n        ';\n    }\n--- request\nGET /lua\n--- more_headers eval\nmy $s;\nmy $i = 1;\nwhile ($i <= 105) {\n    $s .= \"X-$i:$i\\n\";\n    $i++;\n}\n$s\n--- response_body eval\nmy @k;\nif (defined($ENV{TEST_NGINX_USE_HTTP3}) || defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    push @k, \"host: localhost\\n\";\n}\nmy $i = 1;\nwhile ($i <= 105) {\n    push @k, \"x-$i\";\n    $i++;\n}\nif (!defined($ENV{TEST_NGINX_USE_HTTP3}) && !defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    push @k, \"connection: close\\n\";\n    push @k, \"host: localhost\\n\";\n}\n@k = sort @k;\nfor my $k (@k) {\n    if ($k =~ /\\d+/) {\n        $k .= \": $&\\n\";\n    }\n}\nCORE::join(\"\", @k);\n--- timeout: 4\n\n\n\n=== TEST 24: modify subrequest req headers should not affect the parent\n--- config\n    location = /main {\n        rewrite_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            print(\"subrequest: \", res.status)\n        ';\n\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n\n    location /sub {\n        content_by_lua '\n            ngx.req.set_header(\"foo121\", 121)\n            ngx.req.set_header(\"foo122\", 122)\n            ngx.say(\"ok\")\n        ';\n    }\n\n    location = /echo {\n        #echo $echo_client_request_headers;\n        echo \"foo121: [$http_foo121]\";\n        echo \"foo122: [$http_foo122]\";\n    }\n--- request\nGET /main\n--- more_headers\nFoo: foo\nBar: bar\nFoo1: foo1\nFoo2: foo2\nFoo3: foo3\nFoo4: foo4\nFoo5: foo5\nFoo6: foo6\nFoo7: foo7\nFoo8: foo8\nFoo9: foo9\nFoo10: foo10\nFoo11: foo11\nFoo12: foo12\nFoo13: foo13\nFoo14: foo14\nFoo15: foo15\nFoo16: foo16\nFoo17: foo17\nFoo18: foo18\nFoo19: foo19\nFoo20: foo20\n--- response_body\nFoo: []\nBar: []\n--- SKIP\n\n\n\n=== TEST 25: clear_header should clear all the instances of the user custom header\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.req.clear_header(\"Foo\")\n        ';\n\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n\n    location = /echo {\n        echo \"Foo: [$http_foo]\";\n        echo \"Test-Header: [$http_test_header]\";\n    }\n--- request\nGET /t\n--- more_headers\nFoo: foo\nFoo: bah\nTest-Header: 1\n--- response_body\nFoo: []\nTest-Header: [1]\n\n\n\n=== TEST 26: clear_header should clear all the instances of the builtin header\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.req.clear_header(\"Content-Type\")\n        ';\n\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n\n    location = /echo {\n        echo \"Content-Type: [$http_content_type]\";\n        echo \"Test-Header: [$http_test_header]\";\n        #echo $echo_client_request_headers;\n    }\n--- request\nGET /t\n--- more_headers\nContent-Type: foo\nContent-Type: bah\nTest-Header: 1\n--- response_body\nContent-Type: []\nTest-Header: [1]\n\n\n\n=== TEST 27: Converting POST to GET - clearing headers (bug found by Matthieu Tourne, 411 error page)\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.clear_header(\"Content-Type\")\n            ngx.req.clear_header(\"Content-Length\")\n        ';\n\n        #proxy_pass http://127.0.0.1:8888;\n        proxy_pass http://127.0.0.1:$server_port/back;\n    }\n\n    location /back {\n        echo -n $echo_client_request_headers;\n    }\n--- request\nPOST /t\nhello world\n--- more_headers\nContent-Type: application/ocsp-request\nTest-Header: 1\n--- response_body_like eval\nmy $body;\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $body = qr/Connection: close\\r\ntest-header: 1\\r\n\\r\n$/;\n} else {\n    $body = qr/Connection: close\\r\nTest-Header: 1\\r\n\\r\n$/;\n}\n\n$body;\n--- no_error_log\n[error]\n\n\n\n=== TEST 28: clear_header() does not duplicate subsequent headers (old bug)\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.req.clear_header(\"Foo\")\n        ';\n\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n\n    location = /echo {\n        echo $echo_client_request_headers;\n    }\n--- request\nGET /t\n--- more_headers\nBah: bah\nFoo: foo\nTest-Header: 1\nFoo1: foo1\nFoo2: foo2\nFoo3: foo3\nFoo4: foo4\nFoo5: foo5\nFoo6: foo6\nFoo7: foo7\nFoo8: foo8\nFoo9: foo9\nFoo10: foo10\nFoo11: foo11\nFoo12: foo12\nFoo13: foo13\nFoo14: foo14\nFoo15: foo15\nFoo16: foo16\nFoo17: foo17\nFoo18: foo18\nFoo19: foo19\nFoo20: foo20\nFoo21: foo21\nFoo22: foo22\n--- response_body_like eval\nmy $headers;\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $headers = qr/bah: bah\\r\ntest-header: 1\\r\nfoo1: foo1\\r\nfoo2: foo2\\r\nfoo3: foo3\\r\nfoo4: foo4\\r\nfoo5: foo5\\r\nfoo6: foo6\\r\nfoo7: foo7\\r\nfoo8: foo8\\r\nfoo9: foo9\\r\nfoo10: foo10\\r\nfoo11: foo11\\r\nfoo12: foo12\\r\nfoo13: foo13\\r\nfoo14: foo14\\r\nfoo15: foo15\\r\nfoo16: foo16\\r\nfoo17: foo17\\r\nfoo18: foo18\\r\nfoo19: foo19\\r\nfoo20: foo20\\r\nfoo21: foo21\\r\nfoo22: foo22\\r\n/;\n} else {\n    $headers = qr/Bah: bah\\r\nTest-Header: 1\\r\nFoo1: foo1\\r\nFoo2: foo2\\r\nFoo3: foo3\\r\nFoo4: foo4\\r\nFoo5: foo5\\r\nFoo6: foo6\\r\nFoo7: foo7\\r\nFoo8: foo8\\r\nFoo9: foo9\\r\nFoo10: foo10\\r\nFoo11: foo11\\r\nFoo12: foo12\\r\nFoo13: foo13\\r\nFoo14: foo14\\r\nFoo15: foo15\\r\nFoo16: foo16\\r\nFoo17: foo17\\r\nFoo18: foo18\\r\nFoo19: foo19\\r\nFoo20: foo20\\r\nFoo21: foo21\\r\nFoo22: foo22\\r\n/;\n}\n\n$headers;\n\n\n\n=== TEST 29: iterating through headers (raw form)\n--- config\n    location /t {\n        content_by_lua '\n            local h = {}\n            local arr = {}\n            local headers, err = ngx.req.get_headers(nil, true)\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            for k, v in pairs(headers) do\n                h[k] = v\n                table.insert(arr, k)\n            end\n            table.sort(arr)\n            for i, k in ipairs(arr) do\n                ngx.say(k, \": \", h[k])\n            end\n        ';\n    }\n--- request\nGET /t\n--- more_headers\nMy-Foo: bar\nBar: baz\n--- response_body eval\nmy $body;\nif ($ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_HTTP2}) {\n    $body = \"bar: baz\nhost: localhost\nmy-foo: bar\n\";\n} else {\n    $body = \"Bar: baz\nConnection: close\nHost: localhost\nMy-Foo: bar\n\";\n}\n$body;\n\n\n\n=== TEST 30: __index metamethod not working for \"raw\" mode\n--- config\n    location /t {\n        content_by_lua '\n            local h, err = ngx.req.get_headers(nil, true)\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(\"My-Foo-Header: \", h.my_foo_header)\n        ';\n    }\n--- request\nGET /t\n--- more_headers\nMy-Foo-Header: Hello World\n--- response_body\nMy-Foo-Header: nil\n\n\n\n=== TEST 31: __index metamethod not working for the default mode\n--- config\n    location /t {\n        content_by_lua '\n            local h, err = ngx.req.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(\"My-Foo-Header: \", h.my_foo_header)\n        ';\n    }\n--- request\nGET /t\n--- more_headers\nMy-Foo-Header: Hello World\n--- response_body\nMy-Foo-Header: Hello World\n\n\n\n=== TEST 32: clear input header (just more than 20 headers)\n--- config\n    location = /t {\n        rewrite_by_lua 'ngx.req.clear_header(\"R\")';\n        proxy_pass http://127.0.0.1:$server_port/back;\n        proxy_set_header Host foo;\n        #proxy_pass http://127.0.0.1:1234/back;\n    }\n\n    location = /back {\n        echo -n $echo_client_request_headers;\n    }\n--- request\nGET /t\n--- more_headers eval\nmy $s = \"User-Agent: curl\\n\";\n\nfor my $i ('a' .. 'r') {\n    $s .= uc($i) . \": \" . \"$i\\n\"\n}\n$s\n--- response_body eval\nmy $s = \"GET /back HTTP/1.0\\r\nHost: foo\\r\nConnection: close\\r\\n\";\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $s .= \"user-agent: curl\\r\\n\";\n    for my $i ('a' .. 'q') {\n        $s .= $i . \": \" . \"$i\\r\\n\"\n    }\n} else {\n    $s .= \"User-Agent: curl\\r\\n\";\n    for my $i ('a' .. 'q') {\n        $s .= uc($i) . \": \" . \"$i\\r\\n\"\n    }\n}\n\n$s . \"\\r\\n\";\n\n\n\n=== TEST 33: clear input header (just more than 20 headers, and add more)\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.req.clear_header(\"R\")\n            for i = 1, 21 do\n                ngx.req.set_header(\"foo-\" .. i, i)\n            end\n        ';\n        proxy_pass http://127.0.0.1:$server_port/back;\n        proxy_set_header Host foo;\n        #proxy_pass http://127.0.0.1:1234/back;\n    }\n\n    location = /back {\n        echo -n $echo_client_request_headers;\n    }\n--- request\nGET /t\n--- more_headers eval\nmy $s = \"User-Agent: curl\\n\";\n\nfor my $i ('a' .. 'r') {\n    $s .= uc($i) . \": \" . \"$i\\n\"\n}\n$s\n--- response_body eval\nmy $body;\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $body = \"GET /back HTTP/1.0\\r\nHost: foo\\r\nConnection: close\\r\nuser-agent: curl\\r\na: a\\r\nb: b\\r\nc: c\\r\nd: d\\r\ne: e\\r\nf: f\\r\ng: g\\r\nh: h\\r\ni: i\\r\nj: j\\r\nk: k\\r\nl: l\\r\nm: m\\r\nn: n\\r\no: o\\r\np: p\\r\nq: q\\r\nfoo-1: 1\\r\nfoo-2: 2\\r\nfoo-3: 3\\r\nfoo-4: 4\\r\nfoo-5: 5\\r\nfoo-6: 6\\r\nfoo-7: 7\\r\nfoo-8: 8\\r\nfoo-9: 9\\r\nfoo-10: 10\\r\nfoo-11: 11\\r\nfoo-12: 12\\r\nfoo-13: 13\\r\nfoo-14: 14\\r\nfoo-15: 15\\r\nfoo-16: 16\\r\nfoo-17: 17\\r\nfoo-18: 18\\r\nfoo-19: 19\\r\nfoo-20: 20\\r\nfoo-21: 21\\r\n\\r\n\";\n} else {\n    $body = \"GET /back HTTP/1.0\\r\nHost: foo\\r\nConnection: close\\r\nUser-Agent: curl\\r\nA: a\\r\nB: b\\r\nC: c\\r\nD: d\\r\nE: e\\r\nF: f\\r\nG: g\\r\nH: h\\r\nI: i\\r\nJ: j\\r\nK: k\\r\nL: l\\r\nM: m\\r\nN: n\\r\nO: o\\r\nP: p\\r\nQ: q\\r\nfoo-1: 1\\r\nfoo-2: 2\\r\nfoo-3: 3\\r\nfoo-4: 4\\r\nfoo-5: 5\\r\nfoo-6: 6\\r\nfoo-7: 7\\r\nfoo-8: 8\\r\nfoo-9: 9\\r\nfoo-10: 10\\r\nfoo-11: 11\\r\nfoo-12: 12\\r\nfoo-13: 13\\r\nfoo-14: 14\\r\nfoo-15: 15\\r\nfoo-16: 16\\r\nfoo-17: 17\\r\nfoo-18: 18\\r\nfoo-19: 19\\r\nfoo-20: 20\\r\nfoo-21: 21\\r\n\\r\n\";\n}\n\n$body;\n\n\n\n=== TEST 34: clear input header (just more than 21 headers)\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.req.clear_header(\"R\")\n            ngx.req.clear_header(\"Q\")\n        ';\n        proxy_pass http://127.0.0.1:$server_port/back;\n        proxy_set_header Host foo;\n        #proxy_pass http://127.0.0.1:1234/back;\n    }\n\n    location = /back {\n        echo -n $echo_client_request_headers;\n    }\n--- request\nGET /t\n--- more_headers eval\nmy $s = \"User-Agent: curl\\nBah: bah\\n\";\n\nfor my $i ('a' .. 'r') {\n    $s .= uc($i) . \": \" . \"$i\\n\"\n}\n$s\n--- response_body eval\nmy $body;\n\nif ($ENV{TEST_NGINX_USE_HTTP3}) {\n    $body = \"GET /back HTTP/1.0\\r\nHost: foo\\r\nConnection: close\\r\nuser-agent: curl\\r\nbah: bah\\r\na: a\\r\nb: b\\r\nc: c\\r\nd: d\\r\ne: e\\r\nf: f\\r\ng: g\\r\nh: h\\r\ni: i\\r\nj: j\\r\nk: k\\r\nl: l\\r\nm: m\\r\nn: n\\r\no: o\\r\np: p\\r\n\\r\n\"\n} else {\n$body = \"GET /back HTTP/1.0\\r\nHost: foo\\r\nConnection: close\\r\nUser-Agent: curl\\r\nBah: bah\\r\nA: a\\r\nB: b\\r\nC: c\\r\nD: d\\r\nE: e\\r\nF: f\\r\nG: g\\r\nH: h\\r\nI: i\\r\nJ: j\\r\nK: k\\r\nL: l\\r\nM: m\\r\nN: n\\r\nO: o\\r\nP: p\\r\n\\r\n\"\n}\n\n$body;\n\n\n\n=== TEST 35: clear input header (just more than 21 headers)\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.req.clear_header(\"R\")\n            ngx.req.clear_header(\"Q\")\n            for i = 1, 21 do\n                ngx.req.set_header(\"foo-\" .. i, i)\n            end\n        ';\n        proxy_pass http://127.0.0.1:$server_port/back;\n        proxy_set_header Host foo;\n        #proxy_pass http://127.0.0.1:1234/back;\n    }\n\n    location = /back {\n        echo -n $echo_client_request_headers;\n    }\n--- request\nGET /t\n--- more_headers eval\nmy $s = \"User-Agent: curl\\nBah: bah\\n\";\n\nfor my $i ('a' .. 'r') {\n    $s .= uc($i) . \": \" . \"$i\\n\"\n}\n$s\n--- response_body eval\nmy $body;\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $body = \"GET /back HTTP/1.0\\r\nHost: foo\\r\nConnection: close\\r\nuser-agent: curl\\r\nbah: bah\\r\na: a\\r\nb: b\\r\nc: c\\r\nd: d\\r\ne: e\\r\nf: f\\r\ng: g\\r\nh: h\\r\ni: i\\r\nj: j\\r\nk: k\\r\nl: l\\r\nm: m\\r\nn: n\\r\no: o\\r\np: p\\r\nfoo-1: 1\\r\nfoo-2: 2\\r\nfoo-3: 3\\r\nfoo-4: 4\\r\nfoo-5: 5\\r\nfoo-6: 6\\r\nfoo-7: 7\\r\nfoo-8: 8\\r\nfoo-9: 9\\r\nfoo-10: 10\\r\nfoo-11: 11\\r\nfoo-12: 12\\r\nfoo-13: 13\\r\nfoo-14: 14\\r\nfoo-15: 15\\r\nfoo-16: 16\\r\nfoo-17: 17\\r\nfoo-18: 18\\r\nfoo-19: 19\\r\nfoo-20: 20\\r\nfoo-21: 21\\r\n\\r\n\";\n} else {\n    $body = \"GET /back HTTP/1.0\\r\nHost: foo\\r\nConnection: close\\r\nUser-Agent: curl\\r\nBah: bah\\r\nA: a\\r\nB: b\\r\nC: c\\r\nD: d\\r\nE: e\\r\nF: f\\r\nG: g\\r\nH: h\\r\nI: i\\r\nJ: j\\r\nK: k\\r\nL: l\\r\nM: m\\r\nN: n\\r\nO: o\\r\nP: p\\r\nfoo-1: 1\\r\nfoo-2: 2\\r\nfoo-3: 3\\r\nfoo-4: 4\\r\nfoo-5: 5\\r\nfoo-6: 6\\r\nfoo-7: 7\\r\nfoo-8: 8\\r\nfoo-9: 9\\r\nfoo-10: 10\\r\nfoo-11: 11\\r\nfoo-12: 12\\r\nfoo-13: 13\\r\nfoo-14: 14\\r\nfoo-15: 15\\r\nfoo-16: 16\\r\nfoo-17: 17\\r\nfoo-18: 18\\r\nfoo-19: 19\\r\nfoo-20: 20\\r\nfoo-21: 21\\r\n\\r\n\"\n}\n\n$body;\n\n\n\n=== TEST 36: raw form\n--- config\n    location /t {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers(0, true)\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            -- get ALL the raw headers (0 == no limit, not recommended)\n            local h = {}\n            local arr = {}\n            for k, v in pairs(headers) do\n                h[k] = v\n                table.insert(arr, k)\n            end\n            table.sort(arr)\n            for i, k in ipairs(arr) do\n                ngx.say(k, \": \", h[k])\n            end\n        ';\n    }\n--- request\nGET /t\n--- more_headers\nMy-Foo: bar\nBar: baz\n--- response_body eval\nmy $body;\n\nif (defined($ENV{TEST_NGINX_USE_HTTP3})|| defined($ENV{TEST_NGINX_USE_HTTP2})) {\n    $body=\"bar: baz\nhost: localhost\nmy-foo: bar\n\";\n} else {\n    $body=\"Bar: baz\nConnection: close\nHost: localhost\nMy-Foo: bar\n\";\n}\n\n$body;\n--- no_error_log\n[error]\n\n\n\n=== TEST 37: clear X-Real-IP\n--- config\n    location /t {\n        rewrite_by_lua '\n           ngx.req.set_header(\"X-Real-IP\", nil)\n        ';\n        echo \"X-Real-IP: $http_x_real_ip\";\n    }\n--- request\nGET /t\n--- more_headers\nX-Real-IP: 8.8.8.8\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    if (@defined($r->headers_in->x_real_ip) && $r->headers_in->x_real_ip) {\n        printf(\"rewrite: x-real-ip: %s\\n\",\n               user_string_n($r->headers_in->x_real_ip->value->data,\n                             $r->headers_in->x_real_ip->value->len))\n    } else {\n        println(\"rewrite: no x-real-ip\")\n    }\n}\n\nF(ngx_http_core_content_phase) {\n    if (@defined($r->headers_in->x_real_ip) && $r->headers_in->x_real_ip) {\n        printf(\"content: x-real-ip: %s\\n\",\n               user_string_n($r->headers_in->x_real_ip->value->data,\n                             $r->headers_in->x_real_ip->value->len))\n    } else {\n        println(\"content: no x-real-ip\")\n    }\n}\n\n--- stap_out\nrewrite: x-real-ip: 8.8.8.8\ncontent: no x-real-ip\n\n--- response_body\nX-Real-IP: \n\n--- no_error_log\n[error]\n\n\n\n=== TEST 38: set custom X-Real-IP\n--- config\n    location /t {\n        rewrite_by_lua '\n           ngx.req.set_header(\"X-Real-IP\", \"8.8.4.4\")\n        ';\n        echo \"X-Real-IP: $http_x_real_ip\";\n    }\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    if (@defined($r->headers_in->x_real_ip) && $r->headers_in->x_real_ip) {\n        printf(\"rewrite: x-real-ip: %s\\n\",\n               user_string_n($r->headers_in->x_real_ip->value->data,\n                             $r->headers_in->x_real_ip->value->len))\n    } else {\n        println(\"rewrite: no x-real-ip\")\n    }\n\n}\n\nF(ngx_http_core_content_phase) {\n    if (@defined($r->headers_in->x_real_ip) && $r->headers_in->x_real_ip) {\n        printf(\"content: x-real-ip: %s\\n\",\n               user_string_n($r->headers_in->x_real_ip->value->data,\n                             $r->headers_in->x_real_ip->value->len))\n    } else {\n        println(\"content: no x-real-ip\")\n    }\n}\n\n--- stap_out\nrewrite: no x-real-ip\ncontent: x-real-ip: 8.8.4.4\n\n--- response_body\nX-Real-IP: 8.8.4.4\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 39: clear Via\n--- config\n    location /t {\n        rewrite_by_lua '\n           ngx.req.set_header(\"Via\", nil)\n        ';\n        echo \"Via: $http_via\";\n    }\n--- request\nGET /t\n--- more_headers\nVia: 1.0 fred, 1.1 nowhere.com (Apache/1.1)\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    if (@defined($r->headers_in->via) && $r->headers_in->via) {\n        printf(\"rewrite: via: %s\\n\",\n               user_string_n($r->headers_in->via->value->data,\n                             $r->headers_in->via->value->len))\n    } else {\n        println(\"rewrite: no via\")\n    }\n}\n\nF(ngx_http_core_content_phase) {\n    if (@defined($r->headers_in->via) && $r->headers_in->via) {\n        printf(\"content: via: %s\\n\",\n               user_string_n($r->headers_in->via->value->data,\n                             $r->headers_in->via->value->len))\n    } else {\n        println(\"content: no via\")\n    }\n}\n\n--- stap_out\nrewrite: via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)\ncontent: no via\n\n--- response_body\nVia: \n\n--- no_error_log\n[error]\n\n\n\n=== TEST 40: set custom Via\n--- config\n    location /t {\n        rewrite_by_lua '\n           ngx.req.set_header(\"Via\", \"1.0 fred, 1.1 nowhere.com (Apache/1.1)\")\n        ';\n        echo \"Via: $http_via\";\n    }\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    if (@defined($r->headers_in->via) && $r->headers_in->via) {\n        printf(\"rewrite: via: %s\\n\",\n               user_string_n($r->headers_in->via->value->data,\n                             $r->headers_in->via->value->len))\n    } else {\n        println(\"rewrite: no via\")\n    }\n\n}\n\nF(ngx_http_core_content_phase) {\n    if (@defined($r->headers_in->via) && $r->headers_in->via) {\n        printf(\"content: via: %s\\n\",\n               user_string_n($r->headers_in->via->value->data,\n                             $r->headers_in->via->value->len))\n    } else {\n        println(\"content: no via\")\n    }\n}\n\n--- stap_out\nrewrite: no via\ncontent: via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)\n\n--- response_body\nVia: 1.0 fred, 1.1 nowhere.com (Apache/1.1)\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 41: set input header (with underscores in the header name)\n--- config\n    location /req-header {\n        rewrite_by_lua '\n            ngx.req.set_header(\"foo_bar\", \"some value\");\n        ';\n        proxy_pass http://127.0.0.1:$server_port/back;\n    }\n    location = /back {\n        echo -n $echo_client_request_headers;\n    }\n--- request\nGET /req-header\n--- response_body_like eval\nqr{^GET /back HTTP/1.0\\r\nHost: 127.0.0.1:\\d+\\r\nConnection: close\\r\nfoo_bar: some value\\r\n\\r\n$}\n\n\n\n=== TEST 42: HTTP 0.9 (set & get)\n--- config\n    location /foo {\n        content_by_lua '\n            ngx.req.set_header(\"X-Foo\", \"howdy\");\n            local headers, err = ngx.req.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(\"X-Foo: \", headers[\"X-Foo\"])\n        ';\n    }\n--- raw_request eval\n\"GET /foo\\r\\n\"\n--- response_headers\n! X-Foo\n--- response_body\nX-Foo: nil\n--- http09\n--- no_error_log\n[error]\n\n\n\n=== TEST 43: HTTP 0.9 (clear)\n--- config\n    location /foo {\n        content_by_lua '\n            ngx.req.set_header(\"X-Foo\", \"howdy\");\n            local headers, err = ngx.req.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(\"X-Foo: \", headers[\"X-Foo\"])\n        ';\n    }\n--- raw_request eval\n\"GET /foo\\r\\n\"\n--- response_headers\n! X-Foo\n--- response_body\nX-Foo: nil\n--- http09\n--- no_error_log\n[error]\n\n\n\n=== TEST 44: Host header with port and $host (github issue #292)\n--- config\n    location /bar {\n        rewrite_by_lua '\n            ngx.req.set_header(\"Host\", \"agentzh.org:1984\")\n        ';\n        echo \"host var: $host\";\n        echo \"http_host var: $http_host\";\n    }\n--- request\nGET /bar\n--- response_body\nhost var: agentzh.org\nhttp_host var: agentzh.org:1984\n\n\n\n=== TEST 45: Host header with upper case letters and $host (github issue #292)\n--- config\n    location /bar {\n        rewrite_by_lua '\n            ngx.req.set_header(\"Host\", \"agentZH.org:1984\")\n        ';\n        echo \"host var: $host\";\n        echo \"http_host var: $http_host\";\n    }\n--- request\nGET /bar\n--- response_body\nhost var: agentzh.org\nhttp_host var: agentZH.org:1984\n\n\n\n=== TEST 46: clear all and re-insert\n--- config\n    location = /t {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers(100, true)\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            local n = 0\n            for header, _ in pairs(headers) do\n                n = n + 1\n                ngx.req.clear_header(header)\n            end\n            ngx.say(\"got \", n, \" headers\")\n            local i = 0\n            for header, value in pairs(headers) do\n                i = i + 1\n                print(\"1: reinsert header \", header, \": \", i)\n                ngx.req.set_header(header, value)\n            end\n\n            headers, err = ngx.req.get_headers(100, true)\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            n = 0\n            for header, _ in pairs(headers) do\n                n = n + 1\n                ngx.req.clear_header(header)\n            end\n            ngx.say(\"got \", n, \" headers\")\n            -- do return end\n            local i = 0\n            for header, value in pairs(headers) do\n                i = i + 1\n                if i > 8 then\n                    break\n                end\n                print(\"2: reinsert header \", header, \": \", i)\n                ngx.req.set_header(header, value)\n            end\n        ';\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\nCache-Control: max-age=0\\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36\\r\nAccept-Encoding: gzip,deflate,sdch\\r\nAccept-Language: en-US,en;q=0.8\\r\nCookie: test=cookie;\\r\n\\r\n\"\n--- response_body\ngot 8 headers\ngot 8 headers\n--- no_error_log\n[error]\n\n\n\n=== TEST 47: github issue #314: ngx.req.set_header does not override request headers with multiple values\n--- config\n    #lua_code_cache off;\n    location = /t {\n        content_by_lua '\n            ngx.req.set_header(\"AAA\", \"111\")\n            local headers, err = ngx.req.get_headers()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(headers[\"AAA\"])\n        ';\n    }\n--- request\nGET /t\n--- more_headers\nAAA: 123\nAAA: 456\nAAA: 678\n\n--- response_body\n111\n--- no_error_log\n[error]\n\n\n\n=== TEST 48: clear If-Match req header\n--- config\n    location /t {\n        content_by_lua '\n            ngx.req.clear_header(\"if-match\")\n            if not ngx.send_headers() then\n                return\n            end\n            ngx.say(\"test\")\n        ';\n    }\n--- request\nGET /t\n--- more_headers\nIf-Match: abc\n--- response_body\ntest\n--- no_error_log\n[error]\n\n\n\n=== TEST 49: clear If-Unmodified-Since req header\n--- config\n    location /t {\n        content_by_lua '\n            ngx.req.clear_header(\"if-unmodified-since\")\n            ngx.header[\"Last-Modified\"] = \"Tue, 30 Jun 2011 12:16:36 GMT\"\n            if not ngx.send_headers() then\n                return\n            end\n            ngx.say(\"test\")\n        ';\n    }\n--- request\nGET /t\n--- more_headers\nIf-Unmodified-Since: Tue, 28 Jun 2011 12:16:36 GMT\n--- response_body\ntest\n--- no_error_log\n[error]\n\n\n\n=== TEST 50: clear If-None-Match req header\n--- config\n    location /t {\n        content_by_lua '\n            ngx.req.clear_header(\"if-none-match\")\n            -- ngx.header[\"etags\"] = \"abc\"\n            if not ngx.send_headers() then\n                return\n            end\n            ngx.say(\"test\")\n        ';\n    }\n--- request\nGET /t\n--- more_headers\nIf-None-Match: *\n--- response_body\ntest\n--- no_error_log\n[error]\n\n\n\n=== TEST 51: set the Destination request header for WebDav\n--- config\n    location = /a.txt {\n        rewrite_by_lua_block {\n            ngx.req.set_header(\"Destination\", \"/b.txt\")\n        }\n        dav_methods MOVE;\n        dav_access            all:rw;\n        root                  html;\n    }\n\n--- user_files\n>>> a.txt\nhello, world!\n\n--- request\nMOVE /a.txt\n\n--- response_body\n--- no_error_log\nclient sent no \"Destination\" header\n[error]\n--- error_code: 204\n\n\n\n=== TEST 52: X-Forwarded-For\n--- config\n    location = /t {\n        access_by_lua_block {\n            ngx.req.set_header(\"X-Forwarded-For\", \"8.8.8.8\")\n        }\n        proxy_pass http://127.0.0.1:$server_port/back;\n        proxy_set_header Foo $proxy_add_x_forwarded_for;\n    }\n\n    location = /back {\n        echo \"Foo: $http_foo\";\n    }\n\n--- request\nGET /t\n\n--- response_body\nFoo: 8.8.8.8, 127.0.0.1\n--- no_error_log\n[error]\n\n\n\n=== TEST 53: X-Forwarded-For\n--- config\n    location = /t {\n        access_by_lua_block {\n            ngx.req.clear_header(\"X-Forwarded-For\")\n        }\n        proxy_pass http://127.0.0.1:$server_port/back;\n        proxy_set_header Foo $proxy_add_x_forwarded_for;\n    }\n\n    location = /back {\n        echo \"Foo: $http_foo\";\n    }\n\n--- request\nGET /t\n\n--- more_headers\nX-Forwarded-For: 8.8.8.8\n--- response_body\nFoo: 127.0.0.1\n--- no_error_log\n[error]\n\n\n\n=== TEST 54: for bad requests (bad request method letter case)\n--- config\n    error_page 400 = /err;\n\n    location = /err {\n        content_by_lua_block {\n            ngx.req.set_header(\"Foo\", \"bar\")\n            ngx.say(\"ok\")\n        }\n    }\n--- raw_request\nGeT / HTTP/1.1\n--- response_body\nok\n--- no_error_log\n[error]\n--- no_check_leak\n\n\n\n=== TEST 55: for bad requests (bad request method names)\n--- config\n    error_page 400 = /err;\n\n    location = /err {\n        content_by_lua_block {\n            ngx.req.set_header(\"Foo\", \"bar\")\n            ngx.say(\"ok\")\n        }\n    }\n--- raw_request\nGET x HTTP/1.1\n--- response_body\nok\n--- no_error_log\n[error]\n--- no_check_leak\n\n\n\n=== TEST 56: for bad requests causing segfaults when setting & getting multi-value headers\n--- config\n    error_page 400 = /err;\n\n    location = /err {\n        content_by_lua_block {\n            ngx.req.set_header(\"Cookie\", \"foo=bar\")\n            local test = ngx.var.cookie_bar\n\n            ngx.say(\"ok\")\n        }\n    }\n--- raw_request\nGeT / HTTP/1.1\n--- response_body\nok\n--- no_error_log\n[error]\n--- no_check_leak\n\n\n\n=== TEST 57: exceeding custom 3 header limit\n--- config\n    location /lua {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers(3)\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local cnt = 0\n            for key, val in pairs(headers) do\n                cnt = cnt + 1\n            end\n\n            ngx.say(\"found \", cnt, \" headers.\");\n        ';\n    }\n--- request\nGET /lua\n--- more_headers eval\nmy $i = 1;\nmy $s;\nwhile ($i <= 2) {\n    $s .= \"X-$i:$i\\n\";\n    $i++;\n}\n$s\n--- response_body\nerr: truncated\nfound 3 headers.\n--- timeout: 4\n--- error_log\nlua exceeding request header limit 4 > 3\n--- no_error_log\n[error]\n--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3}\n--- no_http2\n\n\n\n=== TEST 58: NOT exceeding custom 3 header limit\n--- config\n    location /lua {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers(3)\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local cnt = 0\n            for key, val in pairs(headers) do\n                cnt = cnt + 1\n            end\n\n            ngx.say(\"found \", cnt, \" headers.\");\n        ';\n    }\n--- request\nGET /lua\n--- more_headers eval\nmy $i = 1;\nmy $s;\nwhile ($i <= 1) {\n    $s .= \"X-$i:$i\\n\";\n    $i++;\n}\n$s\n--- response_body\nfound 3 headers.\n--- timeout: 4\n--- no_error_log\nlua exceeding request header limit\n[error]\n--- skip_eval: 4: $ENV{TEST_NGINX_USE_HTTP3}\n--- no_http2\n\n\n\n=== TEST 59: exceeding custom 3 header limit (raw)\n--- config\n    location /lua {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers(3, true)\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local cnt = 0\n            for key, val in pairs(headers) do\n                cnt = cnt + 1\n            end\n\n            ngx.say(\"found \", cnt, \" headers.\");\n        ';\n    }\n--- request\nGET /lua\n--- more_headers eval\nmy $i = 1;\nmy $s;\nwhile ($i <= 2) {\n    $s .= \"X-$i:$i\\n\";\n    $i++;\n}\n$s\n--- response_body\nerr: truncated\nfound 3 headers.\n--- timeout: 4\n--- error_log\nlua exceeding request header limit 4 > 3\n--- no_error_log\n[error]\n--- skip_eval: 4: $ENV{TEST_NGINX_USE_HTTP3}\n--- no_http2\n\n\n\n=== TEST 60: NOT exceeding custom 3 header limit (raw)\n--- config\n    location /lua {\n        content_by_lua '\n            local headers, err = ngx.req.get_headers(3, true)\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local cnt = 0\n            for key, val in pairs(headers) do\n                cnt = cnt + 1\n            end\n\n            ngx.say(\"found \", cnt, \" headers.\");\n        ';\n    }\n--- request\nGET /lua\n--- more_headers eval\nmy $i = 1;\nmy $s;\nwhile ($i <= 1) {\n    $s .= \"X-$i:$i\\n\";\n    $i++;\n}\n$s\n--- response_body eval\nmy $body;\nif (!defined $ENV{TEST_NGINX_USE_HTTP2}) {\n    $body = \"found 3 headers.\n\";\n} else {\n    $body = \"found 2 headers.\n\";\n}\n\n$body;\n--- timeout: 4\n--- no_error_log\nlua exceeding request header limit\n[error]\n--- skip_eval: 4: $ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 61: setting Host header clears cached $host variable\n--- config\n    location /req-header {\n        # this makes $host indexed and cacheable\n        set $foo $host;\n\n        content_by_lua_block {\n            ngx.say(ngx.var.host)\n            ngx.req.set_header(\"Host\", \"new\");\n            ngx.say(ngx.var.host)\n        }\n    }\n--- request\nGET /req-header\n--- response_body\nlocalhost\nnew\n--- no_error_log\n[error]\n\n\n\n=== TEST 62: unsafe header name (with '\\r')\n--- config\n    location /req-header {\n        rewrite_by_lua_block {\n            ngx.req.set_header(\"Foo\\rfoo\", \"new value\");\n        }\n\n        echo \"Foo: $http_foo\";\n    }\n--- request\nGET /req-header\n--- response_body\nFoo: \n--- no_error_log\n[error]\n\n\n\n=== TEST 63: unsafe header value (with '\\n')\n--- config\n    location /req-header {\n        rewrite_by_lua_block {\n            ngx.req.set_header(\"Foo\", \"new\\nvalue\");\n        }\n\n        echo \"Foo: $http_foo\";\n    }\n--- request\nGET /req-header\n--- response_body\nFoo: new%0Avalue\n--- no_error_log\n[error]\n\n\n\n=== TEST 64: multiple unsafe header values (with '\\n' and '\\t')\n--- config\n    location /req-header {\n        rewrite_by_lua_block {\n            ngx.req.set_header(\"Foo\", { \"new\\nvalue\", \"foo\\tbar\" } );\n        }\n\n        content_by_lua_block {\n            ngx.say(table.concat(ngx.req.get_headers()[\"foo\"], \", \"), \".\")\n        }\n    }\n--- request\nGET /req-header\n--- response_body\nnew%0Avalue, foo\tbar.\n--- no_error_log\n[error]\n\n\n\n=== TEST 65: unsafe names/values logging escapes '\"' and '\\' characters\n--- config\n    location /req-header {\n        rewrite_by_lua_block {\n            ngx.req.set_header(\"Foo\", \"\\\"new\\nvalue\\\\\\\"\");\n        }\n\n        content_by_lua_block {\n            ngx.say(ngx.req.get_headers()[\"foo\"])\n        }\n    }\n--- request\nGET /req-header\n--- response_body\n\"new%0Avalue\\\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 66: add request headers with '\\r\\n'\n--- config\n    location /bar {\n        access_by_lua_block {\n            ngx.req.set_header(\"Foo\\r\", \"123\\r\\n\")\n        }\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n\n    location = /foo {\n        echo $echo_client_request_headers;\n    }\n--- request\nGET /bar\n--- response_body_like chomp\n\\bFoo%0D: 123%0D%0A\\b\n\n\n\n=== TEST 67: add request headers with '\\0'\n--- config\n    location /bar {\n        access_by_lua_block {\n            ngx.req.set_header(\"Foo\", \"\\0\")\n        }\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n\n    location = /foo {\n        echo $echo_client_request_headers;\n    }\n--- request\nGET /bar\n--- response_body_like chomp\n\\bFoo: %00\\b\n\n\n\n=== TEST 68: add request headers with '中文'\n--- config\n    location /bar {\n        access_by_lua_block {\n            ngx.req.set_header(\"Foo中文\", \"ab中文a\")\n        }\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n\n    location = /foo {\n        echo $echo_client_request_headers;\n    }\n--- request\nGET /bar\n--- response_body_like chomp\n\\bFoo%E4%B8%AD%E6%96%87: ab中文a\\r\\n\n"
  },
  {
    "path": "t/029-http-time.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 2);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: http_time in content_by_lua\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(ngx.http_time(1290079655))\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nThu, 18 Nov 2010 11:27:35 GMT\n\n\n\n=== TEST 2: http_time in set_by_lua\n--- config\n    location /lua {\n        set_by_lua $a '\n            return ngx.http_time(1290079655)\n        ';\n        echo $a;\n    }\n--- request\nGET /lua\n--- response_body\nThu, 18 Nov 2010 11:27:35 GMT\n\n\n\n=== TEST 3: parse_http_time in set_by_lua\n--- config\n    location /lua {\n        set_by_lua $a '\n            return ngx.parse_http_time(\"Thu, 18 Nov 2010 11:27:35 GMT\")\n        ';\n        echo $a;\n    }\n--- request\nGET /lua\n--- response_body\n1290079655\n\n\n\n=== TEST 4: parse_http_time in content_by_lua\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(ngx.parse_http_time(\"Thu, 18 Nov 2010 11:27:35 GMT\"))\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n1290079655\n\n\n\n=== TEST 5: bad arg for parse_http_time in content_by_lua\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(ngx.parse_http_time(\"abc\") or \"nil\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nnil\n"
  },
  {
    "path": "t/030-uri-args-with-ctrl.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\nlog_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 3);\n\nno_root_location();\n\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: rewrite args (string with \\r)\n--- config\n    location /foo {\n        rewrite_by_lua_block {\n            ngx.req.set_uri_args(\"a\\rb\")\n        }\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo;\n    }\n    location /echo {\n        content_by_lua_block {\n            ngx.say(ngx.var.request_uri);\n        }\n    }\n--- request\nGET /foo?world\n--- error_code: 200\n--- response_body\n/echo?a%0Db\n\n\n\n=== TEST 2: rewrite args (string with \\n)\n--- config\n    location /foo {\n        rewrite_by_lua_block {\n            ngx.req.set_uri_args(\"a\\nb\")\n        }\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo;\n    }\n    location /echo {\n        content_by_lua_block {\n            ngx.say(ngx.var.request_uri);\n        }\n    }\n--- request\nGET /foo?world\n--- response_body\n/echo?a%0Ab\n\n\n\n=== TEST 3: rewrite args (string with \\0)\n--- config\n    location /foo {\n        rewrite_by_lua_block {\n            ngx.req.set_uri_args(\"a\\0b\")\n        }\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo;\n    }\n    location /echo {\n        content_by_lua_block {\n            ngx.say(ngx.var.request_uri);\n        }\n    }\n--- request\nGET /foo?world\n--- response_body\n/echo?a%00b\n\n\n\n=== TEST 4: rewrite args (string arg with 'lang=中文')\nngx.req.set_uri_args with string argument should be carefully encoded.\nFor backward compatibility, we are allowed to pass such parameters.\n--- config\n    location /foo {\n        rewrite_by_lua_block {\n            ngx.req.set_uri_args(\"lang=中文\")\n        }\n        content_by_lua_block {\n            ngx.say(ngx.var.arg_lang)\n        }\n    }\n--- request\nGET /foo?world\n--- response_body\n中文\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: rewrite args (string arg with '语言=chinese')\nngx.req.set_uri_args with string argument should be carefully encoded.\nFor backward compatibility, we are allowed to pass such parameters.\n--- config\n    location /foo {\n        rewrite_by_lua_block {\n            ngx.req.set_uri_args(\"语言=chinese\")\n        }\n        content_by_lua_block {\n            ngx.say(ngx.var.arg_语言)\n        }\n    }\n--- request\nGET /foo?world\n--- response_body\nchinese\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: rewrite args (string arg with '语言=中文')\nngx.req.set_uri_args with string argument should be carefully encoded.\nFor backward compatibility, we are allowed to pass such parameters.\n--- config\n    location /foo {\n        rewrite_by_lua_block {\n            ngx.req.set_uri_args(\"语言=中文\")\n        }\n        content_by_lua_block {\n            ngx.say(ngx.var.arg_语言)\n        }\n    }\n--- request\nGET /foo?world\n--- response_body\n中文\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/030-uri-args.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\nlog_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\n\nplan tests => repeat_each() * (blocks() * 2 + 23);\n\n\nno_root_location();\n\n#no_shuffle();\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request\nGET /lua?a=3&b=4&c\n--- response_body\na = 3\nb = 4\nc = true\n\n\n\n=== TEST 2: args take no value\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request\nGET /lua?foo&baz=&bar=42\n--- response_body\nbar = 42\nbaz = \nfoo = true\n\n\n\n=== TEST 3: arg key and value escaped\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n\n            ngx.say(\"again...\")\n\n            args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request\nGET /lua?%3d&b%20r=4%61+2\n--- response_body\n= = true\nb r = 4a 2\nagain...\n= = true\nb r = 4a 2\n\n\n\n=== TEST 4: empty\n--- config\n    location /t {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /t\n--- response_body\ndone\n\n\n\n=== TEST 5: empty arg, but with = and &\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua?=&&\n--- response_body\ndone\n\n\n\n=== TEST 6: multi-value keys\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                local val = args[key]\n                if type(val) == \"table\" then\n                    ngx.say(key, \" = [\", table.concat(val, \", \"), \"]\")\n                else\n                    ngx.say(key, \" = \", val)\n                end\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua?foo=32&foo==&foo=baz\n--- response_body\nfoo = [32, =, baz]\ndone\n\n\n\n=== TEST 7: multi-value keys\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                local val = args[key]\n                if type(val) == \"table\" then\n                    ngx.say(key, \" = [\", table.concat(val, \", \"), \"]\")\n                else\n                    ngx.say(key, \" = \", val)\n                end\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua?foo=32&foo==&bar=baz\n--- response_body\nbar = baz\nfoo = [32, =]\ndone\n\n\n\n=== TEST 8: empty arg\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            -- ngx.say(args)\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua?&=\n--- response_body\ndone\n\n\n\n=== TEST 9: = in value\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            -- ngx.say(args)\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua?foo===\n--- response_body\nfoo = ==\ndone\n\n\n\n=== TEST 10: empty key, but non-empty values\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua?=hello&=world\n--- response_body\ndone\n\n\n\n=== TEST 11: updating args with $args\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n\n            ngx.say(\"updating args...\")\n\n            ngx.var.args = \"a=3&b=4\"\n\n            local args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua?foo=bar\n--- response_body\nfoo = bar\nupdating args...\na = 3\nb = 4\ndone\n\n\n\n=== TEST 12: rewrite uri and args\n--- config\n    location /bar {\n        echo $query_string;\n    }\n    location /foo {\n        #set $args 'hello';\n        rewrite_by_lua '\n            ngx.req.set_uri_args(\"hello\")\n            ngx.req.set_uri(\"/bar\", true);\n        ';\n        proxy_pass http://127.0.0.2:12345;\n    }\n--- request\n    GET /foo?world\n--- response_body\nhello\n--- error_log\nlua set uri jump to \"/bar\"\n--- log_level: debug\n\n\n\n=== TEST 13: rewrite args (not break cycle by default)\n--- config\n    location /bar {\n        echo \"bar: $uri?$args\";\n    }\n    location /foo {\n        #set $args 'hello';\n        rewrite_by_lua '\n            ngx.req.set_uri_args(\"hello\")\n            ngx.req.set_uri(\"/bar\", true)\n        ';\n        echo \"foo: $uri?$args\";\n    }\n--- request\n    GET /foo?world\n--- response_body\nbar: /bar?hello\n\n\n\n=== TEST 14: rewrite (not break cycle explicitly)\n--- config\n    location /bar {\n        echo \"bar: $uri?$args\";\n    }\n    location /foo {\n        #set $args 'hello';\n        rewrite_by_lua '\n            ngx.req.set_uri_args(\"hello\")\n            ngx.req.set_uri(\"/bar\", true)\n        ';\n        echo \"foo: $uri?$args\";\n    }\n--- request\n    GET /foo?world\n--- response_body\nbar: /bar?hello\n\n\n\n=== TEST 15: rewrite (break cycle explicitly)\n--- config\n    location /bar {\n        echo \"bar: $uri?$args\";\n    }\n    location /foo {\n        #set $args 'hello';\n        rewrite_by_lua '\n            ngx.req.set_uri(\"/bar\")\n            ngx.req.set_uri_args(\"hello\")\n        ';\n        echo \"foo: $uri?$args\";\n    }\n--- request\n    GET /foo?world\n--- response_body\nfoo: /bar?hello\n\n\n\n=== TEST 16: rewrite uri (zero-length)\n--- config\n    location /foo {\n        #set $args 'hello';\n        rewrite_by_lua '\n            local res, err = pcall(ngx.req.set_uri, \"\")\n            print(\"rewrite: err: \", err)\n        ';\n        content_by_lua '\n            ngx.say(\"foo: \", ngx.var.uri, \"?\", ngx.var.args)\n        ';\n    }\n--- request\n    GET /foo?world\n--- response_body\nfoo: /foo?world\n--- log_level: info\n--- grep_error_log eval: qr/rewrite: .+?(?=,)/\n--- grep_error_log_out\nrewrite: err: attempt to use zero-length uri\n\n\n\n=== TEST 17: rewrite uri and args\n--- config\n    location /bar {\n        echo $server_protocol $query_string;\n    }\n    location /foo {\n        #rewrite ^ /bar?hello? break;\n        rewrite_by_lua '\n            ngx.req.set_uri_args(\"hello\")\n            ngx.req.set_uri(\"/bar\")\n        ';\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT;\n    }\n--- request\n    GET /foo?world\n--- response_body\nHTTP/1.0 hello\n\n\n\n=== TEST 18: rewrite uri and args (table args)\n--- config\n    location /bar {\n        echo $server_protocol $query_string;\n    }\n    location /foo {\n        #rewrite ^ /bar?hello? break;\n        rewrite_by_lua '\n            ngx.req.set_uri(\"/bar\")\n            ngx.req.set_uri_args({[\"ca t\"] = \"%\"})\n        ';\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT;\n    }\n--- request\n    GET /foo?world\n--- response_body\nHTTP/1.0 ca%20t=%25\n\n\n\n=== TEST 19: rewrite uri and args (never returns)\n--- config\n    location /bar {\n        echo $query_string;\n    }\n    location /foo {\n        #set $args 'hello';\n        rewrite_by_lua '\n            ngx.req.set_uri_args(\"hello\")\n            ngx.req.set_uri(\"/bar\", true);\n            ngx.exit(503)\n        ';\n        proxy_pass http://127.0.0.2:12345;\n    }\n--- request\n    GET /foo?world\n--- response_body\nhello\n\n\n\n=== TEST 20: ngx.req.set_uri with jump not allowed in access phase\n--- config\n    location /bar {\n        echo $query_string;\n    }\n    location /foo {\n        #set $args 'hello';\n        set $err '';\n        access_by_lua '\n            local res, err = pcall(ngx.req.set_uri, \"/bar\", true);\n            ngx.var.err = err\n        ';\n        echo \"err: $err\";\n    }\n--- request\n    GET /foo?world\n--- response_body\nerr: API disabled in the context of access_by_lua*\n\n\n\n=== TEST 21: ngx.req.set_uri without jump allowed in access phase\n--- config\n    location /bar {\n        echo $query_string;\n    }\n    location /foo {\n        #set $args 'hello';\n        set $err '';\n        access_by_lua '\n            ngx.req.set_uri(\"/bar\")\n        ';\n        echo \"uri: $uri\";\n    }\n--- request\n    GET /foo?world\n--- response_body\nuri: /bar\n\n\n\n=== TEST 22: ngx.req.set_uri with jump not allowed in content phase\n--- config\n    location /bar {\n        echo $query_string;\n    }\n    location /foo {\n        #set $args 'hello';\n        content_by_lua '\n            local res, err = pcall(ngx.req.set_uri, \"/bar\", true);\n            ngx.say(\"err: \", err)\n        ';\n    }\n--- request\n    GET /foo?world\n--- response_body\nerr: API disabled in the context of content_by_lua*\n\n\n\n=== TEST 23: ngx.req.set_uri without jump allowed in content phase\n--- config\n    location /bar {\n        echo $query_string;\n    }\n    location /foo {\n        #set $args 'hello';\n        set $err '';\n        content_by_lua '\n            ngx.req.set_uri(\"/bar\")\n            ngx.say(\"uri: \", ngx.var.uri)\n        ';\n    }\n--- request\n    GET /foo?world\n--- response_body\nuri: /bar\n\n\n\n=== TEST 24: ngx.req.set_uri with jump not allowed in set_by_lua\n--- config\n    location /bar {\n        echo $query_string;\n    }\n    location /foo {\n        #set $args 'hello';\n        set_by_lua $err '\n            local res, err = pcall(ngx.req.set_uri, \"/bar\", true);\n            return err\n        ';\n        echo \"err: $err\";\n    }\n--- request\n    GET /foo?world\n--- response_body\nerr: API disabled in the context of set_by_lua*\n\n\n\n=== TEST 25: ngx.encode_args (sanity)\n--- config\n    location /lua {\n        set_by_lua $args_str '\n            local t = {a = \"bar\", b = \"foo\"}\n            return ngx.encode_args(t)\n        ';\n        echo $args_str;\n    }\n--- request\nGET /lua\n--- response_body eval\nqr/a=bar&b=foo|b=foo&a=bar/\n\n\n\n=== TEST 26: ngx.encode_args (empty table)\n--- config\n    location /lua {\n        content_by_lua '\n            local t = {a = nil}\n            ngx.say(\"args:\" .. ngx.encode_args(t))\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nargs:\n\n\n\n=== TEST 27: ngx.encode_args (value is table)\n--- config\n    location /lua {\n        content_by_lua '\n            local t = {a = {9, 2}, b = 3}\n            ngx.say(\"args:\" .. ngx.encode_args(t))\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like\n(?x) ^args:\n    (?= .*? \\b a=9 \\b )  # 3 chars\n    (?= .*? \\b a=2 \\b )  # 3 chars\n    (?= .*? \\b b=3 \\b )  # 3 chars\n    (?= (?: [^&]+ & ){2} [^&]+ $ )  # requires exactly 2 &'s\n    (?= .{11} $ )  # requires for total 11 chars (exactly) in the string\n\n\n\n=== TEST 28: ngx.encode_args (boolean values)\n--- config\n    location /lua {\n        content_by_lua '\n            local t = {a = true, foo = 3}\n            ngx.say(\"args: \" .. ngx.encode_args(t))\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like\n^args: (?:a&foo=3|foo=3&a)$\n\n\n\n=== TEST 29: ngx.encode_args (boolean values, false)\n--- config\n    location /lua {\n        content_by_lua '\n            local t = {a = false, foo = 3}\n            ngx.say(\"args: \" .. ngx.encode_args(t))\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nargs: foo=3\n\n\n\n=== TEST 30: boolean values in ngx.encode_args\n--- config\n    location /lua {\n        set_by_lua $args_str '\n            local t = {bar = {32, true}, foo = 3}\n            return ngx.encode_args(t)\n        ';\n        echo $args_str;\n    }\n--- request\nGET /lua\n--- response_body_like\n(?x) ^\n    (?= .*? \\b bar=32 \\b )     # 6 chars\n    (?= .*? \\b bar (?!=) \\b )  # 3 chars\n    (?= .*? \\b foo=3 \\b )      # 5 chars\n    (?= (?: [^&]+ & ){2} [^&]+ $ )  # requires exactly 2 &'s\n    (?= .{16} $ )  # requires for total 16 chars (exactly) in the string\n--- no_error_log\n[error]\n\n\n\n=== TEST 31: ngx.encode_args (bad user data value)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location /lua {\n        content_by_lua '\n            local t = {bar = ngx.shared.dogs, foo = 3}\n            local rc, err = pcall(ngx.encode_args, t)\n            ngx.say(\"rc: \", rc, \", err: \", err)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nrc: false, err: attempt to use userdata as query arg value\n\n\n\n=== TEST 32: ngx.encode_args (empty table)\n--- config\n    location /lua {\n        content_by_lua '\n            local t = {}\n            ngx.say(\"args: \", ngx.encode_args(t))\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nargs: \n\n\n\n=== TEST 33: ngx.encode_args (bad arg)\n--- config\n    location /lua {\n        content_by_lua '\n            local rc, err = pcall(ngx.encode_args, true)\n            ngx.say(\"rc: \", rc, \", err: \", err)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nrc: false, err: bad argument #1 to '?' (table expected, got boolean)\n\n\n\n=== TEST 34: max args (limited after normal key=value)\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args(2)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request\nGET /lua?foo=3&bar=4&baz=2\n--- response_body\nerr: truncated\nbar = 4\nfoo = 3\n--- error_log\nlua hit query args limit 2\n--- log_level: debug\n\n\n\n=== TEST 35: max args (limited after an orphan key)\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args(2)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request\nGET /lua?foo=3&bar&baz=2\n--- response_body\nerr: truncated\nbar = true\nfoo = 3\n--- error_log\nlua hit query args limit 2\n--- log_level: debug\n\n\n\n=== TEST 36: max args (limited after an empty key, but non-empty values)\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args(2)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua?foo=3&=hello&=world\n--- response_body\nerr: truncated\nfoo = 3\ndone\n--- error_log\nlua hit query args limit 2\n--- log_level: debug\n\n\n\n=== TEST 37: default max 100 args\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request eval\nmy $s = \"GET /lua?\";\nmy $i = 1;\nwhile ($i <= 102) {\n    if ($i != 1) {\n        $s .= '&';\n    }\n    $s .= \"a$i=$i\";\n    $i++;\n}\n$s\n--- response_body eval\nmy @k;\nmy $i = 1;\nwhile ($i <= 100) {\n    push @k, \"a$i\";\n    $i++;\n}\n@k = sort @k;\nfor my $k (@k) {\n    if ($k =~ /\\d+/) {\n        $k .= \" = $&\\n\";\n    }\n}\n\n\"err: truncated\\n\" . CORE::join(\"\", @k);\n--- timeout: 4\n--- error_log\nlua hit query args limit 100\n--- log_level: debug\n\n\n\n=== TEST 38: custom max 102 args\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args(102)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request eval\nmy $s = \"GET /lua?\";\nmy $i = 1;\nwhile ($i <= 103) {\n    if ($i != 1) {\n        $s .= '&';\n    }\n    $s .= \"a$i=$i\";\n    $i++;\n}\n$s\n--- response_body eval\nmy @k;\nmy $i = 1;\nwhile ($i <= 102) {\n    push @k, \"a$i\";\n    $i++;\n}\n@k = sort @k;\nfor my $k (@k) {\n    if ($k =~ /\\d+/) {\n        $k .= \" = $&\\n\";\n    }\n}\n\n\"err: truncated\\n\" . CORE::join(\"\", @k);\n--- timeout: 4\n--- error_log\nlua hit query args limit 102\n--- log_level: debug\n\n\n\n=== TEST 39: custom unlimited args\n--- config\n    location /lua {\n        content_by_lua '\n            local args, err = ngx.req.get_uri_args(0)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request eval\nmy $s = \"GET /lua?\";\nmy $i = 1;\nwhile ($i <= 105) {\n    if ($i != 1) {\n        $s .= '&';\n    }\n    $s .= \"a$i=$i\";\n    $i++;\n}\n$s\n--- response_body eval\nmy @k;\nmy $i = 1;\nwhile ($i <= 105) {\n    push @k, \"a$i\";\n    $i++;\n}\n@k = sort @k;\nfor my $k (@k) {\n    if ($k =~ /\\d+/) {\n        $k .= \" = $&\\n\";\n    }\n}\nCORE::join(\"\", @k);\n--- timeout: 4\n\n\n\n=== TEST 40: rewrite uri and args (multi-value args)\n--- config\n    location /bar {\n        echo $server_protocol $query_string;\n    }\n    location /foo {\n        #rewrite ^ /bar?hello? break;\n        rewrite_by_lua '\n            ngx.req.set_uri_args({a = 3, b = {5, 6}})\n            ngx.req.set_uri(\"/bar\")\n        ';\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT;\n    }\n--- request\n    GET /foo?world\n--- response_body eval\nqr/HTTP\\/1.0 (a=3&b=5&b=6|b=5&b=6&a=3|b=6&b=5&a=3)/\n\n\n\n=== TEST 41: ngx.decode_args (sanity)\n--- config\n    location /lua {\n        content_by_lua '\n            local err\n            local args = \"a=bar&b=foo\"\n            args, err = ngx.decode_args(args)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            ngx.say(\"a = \", args.a)\n            ngx.say(\"b = \", args.b)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\na = bar\nb = foo\n\n\n\n=== TEST 42: ngx.decode_args (multi-value)\n--- config\n    location /lua {\n        content_by_lua '\n            local err\n            local args = \"a=bar&b=foo&a=baz\"\n            args, err = ngx.decode_args(args)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            ngx.say(\"a = \", table.concat(args.a, \", \"))\n            ngx.say(\"b = \", args.b)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\na = bar, baz\nb = foo\n\n\n\n=== TEST 43: ngx.decode_args (empty string)\n--- config\n    location /lua {\n        content_by_lua '\n            local err\n            local args = \"\"\n            args, err = ngx.decode_args(args)\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            ngx.say(\"n = \", #args)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nn = 0\n\n\n\n=== TEST 44: ngx.decode_args (boolean args)\n--- config\n    location /lua {\n        content_by_lua '\n            local err\n            local args = \"a&b\"\n            args, err = ngx.decode_args(args)\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            ngx.say(\"a = \", args.a)\n            ngx.say(\"b = \", args.b)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\na = true\nb = true\n\n\n\n=== TEST 45: ngx.decode_args (empty value args)\n--- config\n    location /lua {\n        content_by_lua '\n            local err\n            local args = \"a=&b=\"\n            args, err = ngx.decode_args(args)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            ngx.say(\"a = \", args.a)\n            ngx.say(\"b = \", args.b)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\na = \nb = \n\n\n\n=== TEST 46: ngx.decode_args (max_args = 1)\n--- config\n    location /lua {\n        content_by_lua '\n            local err\n            local args = \"a=bar&b=foo\"\n            args, err = ngx.decode_args(args, 1)\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            ngx.say(\"a = \", args.a)\n            ngx.say(\"b = \", args.b)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nerr: truncated\na = bar\nb = nil\n\n\n\n=== TEST 47: ngx.decode_args (max_args = -1)\n--- config\n    location /lua {\n        content_by_lua '\n            local err\n            local args = \"a=bar&b=foo\"\n            args, err = ngx.decode_args(args, -1)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            ngx.say(\"a = \", args.a)\n            ngx.say(\"b = \", args.b)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\na = bar\nb = foo\n\n\n\n=== TEST 48: ngx.decode_args should not modify lua strings in place\n--- config\n    location /lua {\n        content_by_lua '\n            local s = \"f+f=bar&B=foo\"\n            local args, err = ngx.decode_args(s)\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local arr = {}\n            for k, v in pairs(args) do\n                table.insert(arr, k)\n            end\n            table.sort(arr)\n            for i, k in ipairs(arr) do\n                ngx.say(\"key: \", k)\n            end\n            ngx.say(\"s = \", s)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nkey: B\nkey: f f\ns = f+f=bar&B=foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 49: ngx.decode_args should not modify lua strings in place (sample from Xu Jian)\n--- config\n    lua_need_request_body on;\n    location /t {\n        content_by_lua '\n            local function split(s, delimiter)\n                local result = {}\n                local from = 1\n                local delim_from, delim_to = string.find(s, delimiter, from)\n                while delim_from do\n                    table.insert(result, string.sub(s, from, delim_from - 1))\n                    from = delim_to + 1\n                    delim_from, delim_to = string.find(s, delimiter, from)\n                end\n                table.insert(result, string.sub(s, from))\n                return result\n            end\n\n            local post_data = ngx.req.get_body_data()\n\n            local commands = split(post_data, \"||\")\n            for _, command in pairs(commands) do\n                --command = ngx.unescape_uri(command)\n                local request_args, err = ngx.decode_args(command, 0)\n                if err then\n                    ngx.say(\"err: \", err)\n                end\n\n                local arr = {}\n                for k, v in pairs(request_args) do\n                    table.insert(arr, k)\n                end\n                table.sort(arr)\n                for i, k in ipairs(arr) do\n                    ngx.say(k, \": \", request_args[k])\n                end\n                ngx.say(\" ===============\")\n            end\n        ';\n    }\n--- request\nPOST /t\nmethod=zadd&key=User%3A1227713%3Alikes%3Atwitters&arg1=1356514698&arg2=780984852||method=zadd&key=User%3A1227713%3Alikes%3Atwitters&arg1=1356514698&arg2=780984852||method=zadd&key=User%3A1227713%3Alikes%3Atwitters&arg1=1356514698&arg2=780984852\n--- response_body\narg1: 1356514698\narg2: 780984852\nkey: User:1227713:likes:twitters\nmethod: zadd\n ===============\narg1: 1356514698\narg2: 780984852\nkey: User:1227713:likes:twitters\nmethod: zadd\n ===============\narg1: 1356514698\narg2: 780984852\nkey: User:1227713:likes:twitters\nmethod: zadd\n ===============\n--- no_error_log\n[error]\n\n\n\n=== TEST 50: recursive rewrite\n--- config\n    rewrite_by_lua '\n        local args = ngx.var.args\n        if args == \"jump\" then\n            ngx.req.set_uri(\"/jump\",true)\n        end\n    ';\n\n    location /jump {\n        echo \"Jump around!\";\n    }\n\n    location / {\n        echo \"$scheme://$http_host$request_uri\";\n    }\n--- request\nGET /?jump\n\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n--- no_error_log\n[alert]\n[crit]\n--- error_log\nrewrite or internal redirection cycle while processing \"/jump\"\n--- timeout: 10\n--- log_level: debug\n\n\n\n=== TEST 51: boolean values in ngx.encode_args (trailing arg)\n--- config\n    location /lua {\n        set_by_lua $args_str '\n            local t = {a = {32, true}, foo = 3, bar = 5}\n            return ngx.encode_args(t)\n        ';\n        echo $args_str;\n    }\n--- request\nGET /lua\n--- response_body_like\n(?x) ^\n    (?= .*? \\b a=32 \\b )       # 4 chars\n    (?= .*? \\b a (?=&|$) \\b )  # 1 chars\n    (?= .*? \\b foo=3 \\b )      # 5 chars\n    (?= .*? \\b bar=5 \\b )      # 5 chars\n    (?= (?: [^&]+ & ){3} [^&]+ $ )  # requires exactly 3 &'s\n    (?= .{18} $ )  # requires for total 18 chars (exactly) in the string\n--- no_error_log\n[error]\n\n\n\n=== TEST 52: false boolean values in ngx.encode_args\n--- config\n    location /lua {\n        set_by_lua $args_str '\n            local t = {a = {32, false}, foo = 3, bar = 5}\n            return ngx.encode_args(t)\n        ';\n        echo $args_str;\n    }\n--- request\nGET /lua\n--- response_body_like\n(?x) ^\n    (?= .*? \\b a=32 \\b )   # 4 chars\n    (?= .*? \\b foo=3 \\b )  # 5 chars\n    (?= .*? \\b bar=5 \\b )  # 5 chars\n    (?= (?: [^&]+ & ){2} [^&]+ $ )  # requires exactly 2 &'s\n    (?= .{16} $ )  # requires for total 16 chars (exactly) in the string\n--- no_error_log\n[error]\n\n\n\n=== TEST 53: false boolean values in ngx.encode_args (escaping)\n--- config\n    location /lua {\n        set_by_lua $args_str '\n            local t = {[\"a b\"] = {32, false}, foo = 3, bar = 5}\n            return ngx.encode_args(t)\n        ';\n        echo $args_str;\n    }\n--- request\nGET /lua\n--- response_body_like\n(?x) ^\n    (?= .*? \\b a%20b=32 \\b )  # 8 chars\n    (?= .*? \\b foo=3 \\b )     # 5 chars\n    (?= .*? \\b bar=5 \\b )     # 5 chars\n    (?= (?: [^&]+ & ){2} [^&]+ $ )  # requires exactly 2 &'s\n    (?= .{20} $ )  # requires for total 20 chars (exactly) in the string\n--- no_error_log\n[error]\n\n\n\n=== TEST 54: true boolean values in ngx.encode_args (escaping)\n--- config\n    location /lua {\n        set_by_lua $args_str '\n            local t = {[\"a b\"] = {32, true}, foo = 3, bar = 5}\n            return ngx.encode_args(t)\n        ';\n        echo $args_str;\n    }\n--- request\nGET /lua\n--- response_body_like\n(?x) ^\n    (?= .*? \\b a%20b=32 \\b )     # 8 chars\n    (?= .*? \\b a%20b (?!=) \\b )  # 5 chars\n    (?= .*? \\b foo=3 \\b )        # 5 chars\n    (?= .*? \\b bar=5 \\b )        # 5 chars\n    (?= (?: [^&]+ & ){3} [^&]+ $ )  # requires exactly 3 &'s\n    (?= .{26} $ )  # requires for total 26 chars (exactly) in the string\n--- no_error_log\n[error]\n\n\n\n=== TEST 55: rewrite uri and args (boolean in multi-value args)\n--- config\n    location /bar {\n        echo $server_protocol $query_string;\n    }\n    location /foo {\n        #rewrite ^ /bar?hello? break;\n        rewrite_by_lua '\n            ngx.req.set_uri_args({a = 3, b = {5, true, 6}})\n            ngx.req.set_uri(\"/bar\")\n        ';\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT;\n    }\n--- request\n    GET /foo?world\n--- response_body_like\n(?x) ^HTTP/1.0 \\s\n    (?= .*? \\b a=3 \\b )      # 3 chars\n    (?= .*? \\b b=5 \\b )      # 3 chars\n    (?= .*? \\b b (?!=) \\b )  # 1 chars\n    (?= .*? \\b b=6 \\b )      # 3 chars\n    (?= (?: [^&]+ & ){3} [^&]+ $ )  # requires exactly 3 &'s\n    (?= .{13} $ )  # requires for total 13 chars (exactly) in the string\n\n\n\n=== TEST 56: rewrite uri and args (boolean value)\n--- config\n    location /bar {\n        echo $server_protocol $query_string;\n    }\n    location /foo {\n        #rewrite ^ /bar?hello? break;\n        rewrite_by_lua '\n            ngx.req.set_uri_args({a = 3, b = true})\n            ngx.req.set_uri(\"/bar\")\n        ';\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT;\n    }\n--- request\n    GET /foo?world\n--- response_body_like\n^HTTP/1.0 (a=3&b|b&a=3)$\n\n\n\n=== TEST 57: ngx.encode_args (escaping)\n--- config\n    location /lua {\n        content_by_lua_block {\n            local t = {bar = \"-_.!~*'()\", foo = \",$@|`\"}\n            ngx.say(\"args: \", ngx.encode_args(t))\n        }\n    }\n--- request\nGET /lua\n--- response_body eval\nqr/(\\Qargs: foo=%2C%24%40%7C%60&bar=-_.!~*'()\\E)|(\\Qargs: bar=-_.!~*'()&foo=%2C%24%40%7C%60\\E)/\n--- no_error_log\n[error]\n\n\n\n=== TEST 58: set_uri with unsafe uri (with '\\t')\n--- config\n    location /t {\n        content_by_lua_block {\n            local new_uri = \"/foo\\tbar\"\n            ngx.req.set_uri(new_uri)\n            ngx.say(ngx.var.uri)\n        }\n    }\n--- request\n    GET /t\n--- response\n/foo    bar\n--- no_error_log\n\n\n\n=== TEST 59: set_uri with unsafe uri (with '\\0')\n--- config\n    location /t {\n        content_by_lua_block {\n            local new_uri = '\\0foo'\n            ngx.req.set_uri(new_uri, false, true)\n            ngx.say(ngx.var.uri)\n        }\n    }\n--- request\n    GET /t\n--- error_code: 200\n--- response_body eval\nqr/\\0foo/\n\n\n\n=== TEST 60: set_uri with safe uri (with ' ')\n--- config\n    location /t {\n        rewrite_by_lua_block {\n            local new_uri = \"/foo bar\"\n            ngx.req.set_uri(new_uri)\n        }\n\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT;\n    }\n\n    location /foo {\n        content_by_lua_block {\n            ngx.say(\"request_uri: \", ngx.var.request_uri)\n            ngx.say(\"uri: \", ngx.var.uri)\n        }\n    }\n--- request\n    GET /t\n--- response_body\nrequest_uri: /foo%20bar\nuri: /foo bar\n--- no_error_log\n[error]\n\n\n\n=== TEST 61: set_uri_args with boolean\n--- config\n    location /bar {\n        echo $query_string;\n    }\n    location /foo {\n        #set $args 'hello';\n        rewrite_by_lua_block {\n            ngx.req.set_uri_args(true)\n            ngx.req.set_uri(\"/bar\", true)\n        }\n        proxy_pass http://127.0.0.2:12345;\n    }\n--- request\n    GET /foo?world\n--- response_body_like: 500 Internal Server Error\n--- log_level: debug\n--- error_code: 500\n--- error_log\nbad argument #1 to 'set_uri_args' (string, number, or table expected, but got boolean)\n\n\n\n=== TEST 62: set_uri_args with nil\n--- config\n    location /bar {\n        echo $query_string;\n    }\n    location /foo {\n        #set $args 'hello';\n        rewrite_by_lua_block {\n            ngx.req.set_uri_args(nil)\n            ngx.req.set_uri(\"/bar\", true)\n        }\n        proxy_pass http://127.0.0.2:12345;\n    }\n--- request\n    GET /foo?world\n--- response_body_like: 500 Internal Server Error\n--- log_level: debug\n--- error_code: 500\n--- error_log\nbad argument #1 to 'set_uri_args' (string, number, or table expected, but got nil)\n\n\n\n=== TEST 63: set_uri_args with userdata\n--- config\n    location /bar {\n        echo $query_string;\n    }\n    location /foo {\n        #set $args 'hello';\n        rewrite_by_lua_block {\n            ngx.req.set_uri_args(ngx.null)\n            ngx.req.set_uri(\"/bar\", true)\n        }\n        proxy_pass http://127.0.0.2:12345;\n    }\n--- request\n    GET /foo?world\n--- response_body_like: 500 Internal Server Error\n--- log_level: debug\n--- error_code: 500\n--- error_log\nbad argument #1 to 'set_uri_args' (string, number, or table expected, but got userdata)\n\n\n\n=== TEST 64: set_uri binary option with unsafe uri\nexplicit specify binary option to true\n--- config\n    location /t {\n        rewrite_by_lua_block {\n            local new_uri = \"/foo\\r\\nbar\"\n            ngx.req.set_uri(new_uri, false, true)\n        }\n\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT;\n    }\n\n    location /foo {\n        content_by_lua_block {\n            ngx.say(\"request_uri: \", ngx.var.request_uri)\n            ngx.say(\"uri: \", ngx.var.uri)\n        }\n    }\n--- request\n    GET /t\n--- response_body eval\n[\"request_uri: /foo%0D%0Abar\\nuri: /foo\\r\\nbar\\n\", \"request_uri: /foo%0D%0Abar\\nuri: /foo\\r\\nbar\\n\"]\n--- no_error_log\n[error]\n\n\n\n=== TEST 65: set_uri binary option with unsafe uri\nexplicit specify binary option to false\n--- config\n    location /t {\n        rewrite_by_lua_block {\n            local new_uri = \"/foo\\r\\nbar\"\n            ngx.req.set_uri(new_uri, false, false)\n        }\n\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT;\n    }\n\n    location /foo {\n        content_by_lua_block {\n            ngx.say(\"request_uri: \", ngx.var.request_uri)\n            ngx.say(\"uri: \", ngx.var.uri)\n        }\n    }\n--- request\n    GET /t\n--- error_code: 500\n--- error_log eval\nqr{\\[error\\] \\d+#\\d+: \\*\\d+ lua entry thread aborted: runtime error: rewrite_by_lua\\(nginx.conf:\\d+\\):\\d+: unsafe byte \"0x0d\" in uri \"/foo\\\\x0D\\\\x0Abar\" \\(maybe you want to set the 'binary' argument\\?\\)}\n\n\n\n=== TEST 66: set_uri binary option with safe uri\nexplicit specify binary option to false\n--- config\n    location /t {\n        rewrite_by_lua_block {\n            local new_uri = \"/foo bar\"\n            ngx.req.set_uri(new_uri, false, true)\n        }\n\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT;\n    }\n\n    location /foo {\n        content_by_lua_block {\n            ngx.say(\"request_uri: \", ngx.var.request_uri)\n            ngx.say(\"uri: \", ngx.var.uri)\n        }\n    }\n--- request\n    GET /t\n--- response_body\nrequest_uri: /foo%20bar\nuri: /foo bar\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/031-post-args.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 6);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /lua {\n        lua_need_request_body on;\n        content_by_lua '\n            local args, err = ngx.req.get_post_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request\nPOST /lua\na=3&b=4&c\n--- response_body\na = 3\nb = 4\nc = true\n\n\n\n=== TEST 2: lua_need_request_body off\n--- config\n    location /lua {\n        lua_need_request_body off;\n        content_by_lua '\n            local args, err = ngx.req.get_post_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request\nPOST /lua\na=3&b=4&c\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 3: empty request body\n--- config\n    location /lua {\n        lua_need_request_body on;\n        content_by_lua '\n            local args, err = ngx.req.get_post_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                local val = args[key]\n                if type(val) == \"table\" then\n                    ngx.say(key, \": \", table.concat(val, \", \"))\n                else\n                    ngx.say(key, \": \", val)\n                end\n            end\n        ';\n    }\n--- request\nPOST /lua\n--- response_body\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 4: max args (limited after normal key=value)\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.req.read_body();\n            local args, err = ngx.req.get_post_args(2)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request\nPOST /lua\nfoo=3&bar=4&baz=2\n--- response_body\nerr: truncated\nbar = 4\nfoo = 3\n--- error_log\nlua hit query args limit 2\n\n\n\n=== TEST 5: max args (limited after an orphan key)\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.req.read_body();\n            local args, err = ngx.req.get_post_args(2)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request\nPOST /lua\nfoo=3&bar&baz=2\n--- response_body\nerr: truncated\nbar = true\nfoo = 3\n--- error_log\nlua hit query args limit 2\n\n\n\n=== TEST 6: max args (limited after an empty key, but non-empty values)\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.req.read_body();\n            local args, err = ngx.req.get_post_args(2)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nPOST /lua\nfoo=3&=hello&=world\n--- response_body\nerr: truncated\nfoo = 3\ndone\n--- error_log\nlua hit query args limit 2\n\n\n\n=== TEST 7: default max 100 args\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.req.read_body();\n            local args, err = ngx.req.get_post_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request eval\nmy $s = \"POST /lua\\n\";\nmy $i = 1;\nwhile ($i <= 102) {\n    if ($i != 1) {\n        $s .= '&';\n    }\n    $s .= \"a$i=$i\";\n    $i++;\n}\n$s\n--- response_body eval\nmy @k;\nmy $i = 1;\nwhile ($i <= 100) {\n    push @k, \"a$i\";\n    $i++;\n}\n@k = sort @k;\nfor my $k (@k) {\n    if ($k =~ /\\d+/) {\n        $k .= \" = $&\\n\";\n    }\n}\n\"err: truncated\\n\" . CORE::join(\"\", @k);\n--- timeout: 4\n--- error_log\nlua hit query args limit 100\n\n\n\n=== TEST 8: custom max 102 args\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.req.read_body()\n            local args, err = ngx.req.get_post_args(102)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request eval\nmy $s = \"POST /lua\\n\";\nmy $i = 1;\nwhile ($i <= 103) {\n    if ($i != 1) {\n        $s .= '&';\n    }\n    $s .= \"a$i=$i\";\n    $i++;\n}\n$s\n--- response_body eval\nmy @k;\nmy $i = 1;\nwhile ($i <= 102) {\n    push @k, \"a$i\";\n    $i++;\n}\n@k = sort @k;\nfor my $k (@k) {\n    if ($k =~ /\\d+/) {\n        $k .= \" = $&\\n\";\n    }\n}\n\"err: truncated\\n\" . CORE::join(\"\", @k);\n--- timeout: 4\n--- error_log\nlua hit query args limit 102\n\n\n\n=== TEST 9: custom unlimited args\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.req.read_body()\n            local args, err = ngx.req.get_post_args(0)\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                ngx.say(key, \" = \", args[key])\n            end\n        ';\n    }\n--- request eval\nmy $s = \"POST /lua\\n\";\nmy $i = 1;\nwhile ($i <= 105) {\n    if ($i != 1) {\n        $s .= '&';\n    }\n    $s .= \"a$i=$i\";\n    $i++;\n}\n$s\n--- response_body eval\nmy @k;\nmy $i = 1;\nwhile ($i <= 105) {\n    push @k, \"a$i\";\n    $i++;\n}\n@k = sort @k;\nfor my $k (@k) {\n    if ($k =~ /\\d+/) {\n        $k .= \" = $&\\n\";\n    }\n}\nCORE::join(\"\", @k);\n--- timeout: 4\n\n\n\n=== TEST 10: request body in temp file\n--- config\n    location /lua {\n        lua_need_request_body on;\n        client_body_in_file_only clean;\n        content_by_lua_block {\n            local args, err = ngx.req.get_post_args()\n\n            if err then\n                ngx.say(\"err: \", err)\n            end\n\n            if args then\n                local keys = {}\n                for key, val in pairs(args) do\n                    table.insert(keys, key)\n                end\n\n                table.sort(keys)\n                for i, key in ipairs(keys) do\n                    ngx.say(key, \" = \", args[key])\n                end\n            end\n        }\n    }\n--- request\nPOST /lua\na=3&b=4&c\n--- response_body\nerr: request body in temp file not supported\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/032-iolist.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 2);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /lua {\n        content_by_lua '\n            local table = {\"hello\", nil, true, false, 32.5, 56}\n            ngx.say(table)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nhelloniltruefalse32.556\n\n\n\n=== TEST 2: nested table\n--- config\n    location /lua {\n        content_by_lua '\n            local table = {\"hello\", nil, true, false, 32.5, 56}\n            local table2 = {table, \"--\", table}\n            ngx.say(table2)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nhelloniltruefalse32.556--helloniltruefalse32.556\n\n\n\n=== TEST 3: non-array table\n--- config\n    location /lua {\n        content_by_lua '\n            local table = {foo = 3}\n            ngx.say(table)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n\n\n\n=== TEST 4: bad data type in table\n--- config\n    location /lua {\n        content_by_lua '\n            local f = function () return end\n            local table = {1, 3, f}\n            ngx.say(table)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n"
  },
  {
    "path": "t/033-ctx.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3 + 8);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.ctx.foo = 32;\n            ngx.say(ngx.ctx.foo)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n32\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: rewrite, access, and content\n--- config\n    location /lua {\n        rewrite_by_lua '\n            print(\"foo = \", ngx.ctx.foo)\n            ngx.ctx.foo = 76\n        ';\n        access_by_lua '\n            ngx.ctx.foo = ngx.ctx.foo + 3\n        ';\n        content_by_lua '\n            ngx.say(ngx.ctx.foo)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n79\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/foo = [^,]+/\n--- log_level: info\n--- grep_error_log_out\nfoo = nil\n\n\n\n=== TEST 3: interal redirect clears ngx.ctx\n--- config\n    location /echo {\n        content_by_lua '\n            ngx.say(ngx.ctx.foo)\n        ';\n    }\n    location /lua {\n        content_by_lua '\n            ngx.ctx.foo = ngx.var.arg_data\n            -- ngx.say(ngx.ctx.foo)\n            ngx.exec(\"/echo\")\n        ';\n    }\n--- request\nGET /lua?data=hello\n--- response_body\nnil\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: subrequest has its own ctx\n--- config\n    location /sub {\n        content_by_lua '\n            ngx.say(\"sub pre: \", ngx.ctx.blah)\n            ngx.ctx.blah = 32\n            ngx.say(\"sub post: \", ngx.ctx.blah)\n        ';\n    }\n    location /main {\n        content_by_lua '\n            ngx.ctx.blah = 73\n            ngx.say(\"main pre: \", ngx.ctx.blah)\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n            ngx.say(\"main post: \", ngx.ctx.blah)\n        ';\n    }\n--- request\n    GET /main\n--- response_body\nmain pre: 73\nsub pre: nil\nsub post: 32\nmain post: 73\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: overriding ctx\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.ctx = { foo = 32, bar = 54 };\n            ngx.say(ngx.ctx.foo)\n            ngx.say(ngx.ctx.bar)\n\n            ngx.ctx = { baz = 56  };\n            ngx.say(ngx.ctx.foo)\n            ngx.say(ngx.ctx.baz)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n32\n54\nnil\n56\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: header filter\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.ctx.foo = 32;\n            ngx.say(ngx.ctx.foo)\n        ';\n        header_filter_by_lua '\n            ngx.header.blah = ngx.ctx.foo + 1\n        ';\n    }\n--- request\nGET /lua\n--- response_headers\nblah: 33\n--- response_body\n32\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: capture_multi\n--- config\n    location /other {\n        content_by_lua '\n            ngx.say(\"dog = \", ngx.ctx.dog)\n        ';\n    }\n\n    location /lua {\n        set $dog 'blah';\n        set $cat 'foo';\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                {\"/other/1\",\n                    { ctx = { dog = \"hello\" }}\n                },\n                {\"/other/2\",\n                    { ctx = { dog = \"hiya\" }}\n                }\n            };\n\n            ngx.print(res1.body)\n            ngx.print(res2.body)\n            ngx.say(\"parent: \", ngx.ctx.dog)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ndog = hello\ndog = hiya\nparent: nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: set_by_lua\n--- config\n    location /lua {\n        set_by_lua $bar 'ngx.ctx.foo = 3 return 4';\n        set_by_lua $foo 'return ngx.ctx.foo';\n        echo \"foo = $foo, bar = $bar\";\n    }\n--- request\nGET /lua\n--- response_body\nfoo = 3, bar = 4\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: ngx.ctx leaks with ngx.exec + log_by_lua\n--- config\n    location = /t {\n        content_by_lua '\n            ngx.ctx.foo = 32;\n            ngx.exec(\"/f\")\n        ';\n        log_by_lua 'ngx.log(ngx.WARN, \"ctx.foo = \", ngx.ctx.foo)';\n    }\n    location = /f {\n        content_by_lua '\n            ngx.say(ngx.ctx.foo)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nnil\n--- no_error_log\n[error]\nctx.foo = \n\n\n\n=== TEST 10: memory leaks with ngx.ctx + ngx.req.set_uri + log_by_lua\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.ctx.foo = 32;\n            ngx.req.set_uri(\"/f\", true)\n        ';\n        log_by_lua 'ngx.log(ngx.WARN, \"ctx.foo = \", ngx.ctx.foo)';\n    }\n    location = /f {\n        content_by_lua '\n            ngx.say(ngx.ctx.foo)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nnil\n--- no_error_log\n[error]\nctx.foo = \n\n\n\n=== TEST 11: ngx.ctx + ngx.exit(ngx.ERROR) + log_by_lua\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.ctx.foo = 32;\n            ngx.exit(ngx.ERROR)\n        ';\n        log_by_lua 'ngx.log(ngx.WARN, \"ngx.ctx = \", ngx.ctx.foo)';\n    }\n--- request\nGET /t\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nngx.ctx = 32\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 12: ngx.ctx + ngx.exit(200) + log_by_lua\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.ctx.foo = 32;\n            ngx.say(ngx.ctx.foo)\n            ngx.exit(200)\n        ';\n        log_by_lua 'ngx.log(ngx.WARN, \"ctx.foo = \", ngx.ctx.foo)';\n    }\n--- request\nGET /t\n--- response_body\n32\n--- no_error_log\n[error]\n--- error_log\nctx.foo = 32\n\n\n\n=== TEST 13: ngx.ctx + ngx.redirect + log_by_lua\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.ctx.foo = 32;\n            ngx.redirect(\"/f\")\n        ';\n        log_by_lua 'ngx.log(ngx.WARN, \"ngx.ctx.foo = \", 32)';\n    }\n--- request\nGET /t\n--- response_body_like: 302 Found\n--- error_code: 302\n--- error_log\nctx.foo = 32\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: set ngx.ctx before internal redirects performed by other nginx modules\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.ctx.foo = \"hello world\";\n        ';\n        echo_exec /foo;\n    }\n\n    location = /foo {\n        echo hello;\n    }\n--- request\nGET /t\n--- response_body\nhello\n--- no_error_log\n[error]\n--- log_level: debug\n--- error_log\nlua release ngx.ctx at ref\n\n\n\n=== TEST 15: set ngx.ctx before internal redirects performed by other nginx modules (with log_by_lua)\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.ctx.foo = \"hello world\";\n        ';\n        echo_exec /foo;\n    }\n\n    location = /foo {\n        echo hello;\n        log_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nhello\n--- no_error_log\n[error]\n--- log_level: debug\n--- error_log\nlua release ngx.ctx at ref\n\n\n\n=== TEST 16: set ngx.ctx before simple uri rewrite performed by other nginx modules\n--- config\n    location = /t {\n        set_by_lua $a 'ngx.ctx.foo = \"hello world\"; return 1';\n        rewrite ^ /foo last;\n        echo blah;\n    }\n\n    location = /foo {\n        echo foo;\n    }\n--- request\nGET /t\n--- response_body\nfoo\n--- no_error_log\n[error]\n--- log_level: debug\n--- error_log\nlua release ngx.ctx at ref\n\n\n\n=== TEST 17: ngx.ctx gets prematurely released ngx.exit()\n--- config\n    location = /t {\n        rewrite_by_lua '\n            ngx.ctx.foo = 3\n        ';\n        content_by_lua '\n            -- if ngx.headers_sent ~= true then ngx.send_headers() end\n            return ngx.exit(200)\n        ';\n        header_filter_by_lua '\n            if ngx.ctx.foo ~= 3 then\n                ngx.log(ngx.ERR, \"bad ngx.ctx.foo: \", ngx.ctx.foo)\n            end\n        ';\n        }\n--- request\n    GET /t\n--- response_body\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: ngx.ctx gets prematurely released ngx.exit() (lua_code_cache off)\n--- config\n    location = /t {\n        lua_code_cache off;\n        rewrite_by_lua '\n            ngx.ctx.foo = 3\n        ';\n        content_by_lua '\n            -- if ngx.headers_sent ~= true then ngx.send_headers() end\n            return ngx.exit(200)\n        ';\n        header_filter_by_lua '\n            if ngx.ctx.foo ~= 3 then\n                ngx.log(ngx.ERR, \"bad ngx.ctx.foo: \", ngx.ctx.foo)\n            end\n        ';\n        }\n--- request\n    GET /t\n--- response_body\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/034-match.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 16);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]+)\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n\n=== TEST 2: escaping sequences\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"(\\\\\\\\d+)\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n\n=== TEST 3: escaping sequences (bad)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"(\\\\d+)\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body_like: 500 Internal Server Error\n--- error_log eval\n[qr/invalid escape sequence near '\"\\('/]\n--- error_code: 500\n\n\n\n=== TEST 4: escaping sequences in [[ ... ]]\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"[[\\\\d+]]\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body_like: 500 Internal Server Error\n--- error_log eval\n[qr/invalid escape sequence near '\"\\[\\['/]\n--- error_code: 500\n\n\n\n=== TEST 5: single capture\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]{2})[0-9]+\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n12\n\n\n\n=== TEST 6: multiple captures\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([a-z]+).*?([0-9]{2})[0-9]+\", \"\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, 1234\nhello\n12\n\n\n\n=== TEST 7: multiple captures (with o)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([a-z]+).*?([0-9]{2})[0-9]+\", \"o\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, 1234\nhello\n12\n\n\n\n=== TEST 8: not matched\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"foo\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched: nil\n\n\n\n=== TEST 9: case sensitive by default\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"HELLO\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched: nil\n\n\n\n=== TEST 10: case insensitive\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"HELLO\", \"i\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\n\n\n\n=== TEST 11: UTF-8 mode\n--- config\n    location /re {\n        content_by_lua '\n            local rc, err = pcall(ngx.re.match, \"hello章亦春\", \"HELLO.{2}\", \"iu\")\n            if not rc then\n                ngx.say(\"FAIL: \", err)\n                return\n            end\n            local m = err\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body_like chop\n^(?:FAIL: bad argument \\#2 to '\\?' \\(pcre_compile\\(\\) failed: this version of PCRE is not compiled with PCRE_UTF8 support in \"HELLO\\.\\{2\\}\" at \"HELLO\\.\\{2\\}\"\\)|hello章亦)$\n\n\n\n=== TEST 12: multi-line mode (^ at line head)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\\\\nworld\", \"^world\", \"m\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nworld\n\n\n\n=== TEST 13: multi-line mode (. does not match \\n)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\\\\nworld\", \".*\", \"m\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\n\n\n\n=== TEST 14: single-line mode (^ as normal)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\\\\nworld\", \"^world\", \"s\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched: nil\n\n\n\n=== TEST 15: single-line mode (dot all)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\\\\nworld\", \".*\", \"s\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nworld\n\n\n\n=== TEST 16: extended mode (ignore whitespaces)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\\\\nworld\", \"\\\\\\\\w     \\\\\\\\w\", \"x\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhe\n\n\n\n=== TEST 17: bad pattern\n--- config\n    location /re {\n        content_by_lua '\n            local m, err = ngx.re.match(\"hello\\\\nworld\", \"(abc\")\n            if m then\n                ngx.say(m[0])\n\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: bad option\n--- config\n    location /re {\n        content_by_lua '\n            local rc, m = pcall(ngx.re.match, \"hello\\\\nworld\", \".*\", \"Hm\")\n            if rc then\n                if m then\n                    ngx.say(m[0])\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            else\n                ngx.say(\"error: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body_like chop\nerror: .*?unknown flag \"H\" \\(flags \"Hm\"\\)\n\n\n\n=== TEST 19: extended mode (ignore whitespaces)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, world\", \"(world)|(hello)\", \"x\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nfalse\nhello\n\n\n\n=== TEST 20: optional trailing captures\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]+)(h?)\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n\"1234\n1234\n\n\"\n\n\n\n=== TEST 21: anchored match (failed)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]+)\", \"a\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched!\n\n\n\n=== TEST 22: anchored match (succeeded)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"1234, hello\", \"([0-9]+)\", \"a\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n\n=== TEST 23: match with ctx but no pos\n--- config\n    location /re {\n        content_by_lua '\n            local ctx = {}\n            local m = ngx.re.match(\"1234, hello\", \"([0-9]+)\", \"\", ctx)\n            if m then\n                ngx.say(m[0])\n                ngx.say(ctx.pos)\n            else\n                ngx.say(\"not matched!\")\n                ngx.say(ctx.pos)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n5\n\n\n\n=== TEST 24: match with ctx and a pos\n--- config\n    location /re {\n        content_by_lua '\n            local ctx = { pos = 3 }\n            local m = ngx.re.match(\"1234, hello\", \"([0-9]+)\", \"\", ctx)\n            if m then\n                ngx.say(m[0])\n                ngx.say(ctx.pos)\n            else\n                ngx.say(\"not matched!\")\n                ngx.say(ctx.pos)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n34\n5\n\n\n\n=== TEST 25: sanity (set_by_lua)\n--- config\n    location /re {\n        set_by_lua $res '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]+)\")\n            if m then\n                return m[0]\n            else\n                return \"not matched!\"\n            end\n        ';\n        echo $res;\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n\n=== TEST 26: match (look-behind assertion)\n--- config\n    location /re {\n        content_by_lua '\n            local ctx = {}\n            local m = ngx.re.match(\"{foobarbaz}\", \"(?<=foo)bar|(?<=bar)baz\", \"\", ctx)\n            ngx.say(m and m[0])\n\n            m = ngx.re.match(\"{foobarbaz}\", \"(?<=foo)bar|(?<=bar)baz\", \"\", ctx)\n            ngx.say(m and m[0])\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nbar\nbaz\n\n\n\n=== TEST 27: escaping sequences\n--- config\n    location /re {\n        content_by_lua_file html/a.lua;\n    }\n--- user_files\n>>> a.lua\nlocal m = ngx.re.match(\"hello, 1234\", \"(\\\\\\s+)\")\nif m then\n    ngx.say(\"[\", m[0], \"]\")\nelse\n    ngx.say(\"not matched!\")\nend\n--- request\n    GET /re\n--- response_body_like: 500 Internal Server Error\n--- error_log eval\n[qr/invalid escape sequence near '\"\\(\\\\'/]\n--- error_code: 500\n\n\n\n=== TEST 28: escaping sequences\n--- config\n    location /re {\n        access_by_lua_file html/a.lua;\n        content_by_lua return;\n    }\n--- user_files\n>>> a.lua\nlocal uri = \"<impact>2</impact>\"\nlocal regex = '(?:>[\\\\w\\\\s]*</?\\\\w{2,}>)';\nngx.say(\"regex: \", regex)\nlocal m = ngx.re.match(uri, regex, \"oi\")\nif m then\n    ngx.say(\"[\", m[0], \"]\")\nelse\n    ngx.say(\"not matched!\")\nend\n--- request\n    GET /re\n--- response_body\nregex: (?:>[\\w\\s]*</?\\w{2,}>)\n[>2</impact>]\n\n\n\n=== TEST 29: long brackets\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", [[\\\\d+]])\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n\n=== TEST 30: bad pattern\n--- config\n    location /re {\n        content_by_lua '\n            local m, err = ngx.re.match(\"hello, 1234\", \"([0-9]+\")\n            if m then\n                ngx.say(m[0])\n\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n\n                else\n                    ngx.say(\"not matched!\")\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile\\(\\) failed: missing closing parenthesis in \\\"\\([0-9]+\\\"\\n\"\n:\n\"error: pcre_compile\\(\\) failed: missing \\) in \\\"\\([0-9]+\\\"\\n\"\n\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 31: long brackets containing [...]\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", [[([0-9]+)]])\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n\n=== TEST 32: bug report (github issue #72)\n--- config\n    location /re {\n        content_by_lua '\n            ngx.re.match(\"hello\", \"hello\", \"j\")\n            ngx.say(\"done\")\n        ';\n        header_filter_by_lua '\n            ngx.re.match(\"hello\", \"world\", \"j\")\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ndone\n\n\n\n=== TEST 33: bug report (github issue #72)\n--- config\n    location /re {\n        content_by_lua '\n            ngx.re.match(\"hello\", \"hello\", \"j\")\n            ngx.exec(\"/foo\")\n        ';\n    }\n\n    location /foo {\n        content_by_lua '\n            ngx.re.match(\"hello\", \"world\", \"j\")\n            ngx.say(\"done\")\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ndone\n\n\n\n=== TEST 34: non-empty subject, empty pattern\n--- config\n    location /re {\n        content_by_lua '\n            local ctx = {}\n            local m = ngx.re.match(\"hello, 1234\", \"\", \"\", ctx)\n            if m then\n                ngx.say(\"pos: \", ctx.pos)\n                ngx.say(\"m: \", m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\npos: 1\nm: \n--- no_error_log\n[error]\n\n\n\n=== TEST 35: named subpatterns w/ extraction\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"(?<first>[a-z]+), [0-9]+\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m.first)\n                ngx.say(m.second)\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, 1234\nhello\nhello\nnil\n\n\n\n=== TEST 36: duplicate named subpatterns w/ extraction\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"(?<first>[a-z]+), (?<first>[0-9]+)\", \"D\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n                ngx.say(table.concat(m.first,\"-\"))\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, 1234\nhello\n1234\nhello-1234\n\n\n\n=== TEST 37: named captures are empty strings\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"1234\", \"(?<first>[a-z]*)([0-9]+)\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m.first)\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n1234\n\n\n\n=== TEST 38: named captures are false\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, world\", \"(world)|(hello)|(?<named>howdy)\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n                ngx.say(m[3])\n                ngx.say(m[\"named\"])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nfalse\nhello\nfalse\nfalse\n\n\n\n=== TEST 39: duplicate named subpatterns\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, world\",\n                                   \"(?<named>\\\\\\\\w+), (?<named>\\\\\\\\w+)\",\n                                   \"D\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n                ngx.say(table.concat(m.named,\"-\"))\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world\nhello\nworld\nhello-world\n--- no_error_log\n[error]\n\n\n\n=== TEST 40: Javascript compatible mode\n--- config\n    location /t {\n        content_by_lua '\n            local m = ngx.re.match(\"章\", [[\\\\u7AE0]], \"uJ\")\n            if m then\n                ngx.say(\"matched: \", m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nmatched: 章\n--- no_error_log\n[error]\n\n\n\n=== TEST 41: empty duplicate captures\n--- config\n    location = /t {\n        content_by_lua \"\n            local target = 'test'\n            local regex = '^(?:(?<group1>(?:foo))|(?<group2>(?:bar))|(?<group3>(?:test)))$'\n\n            -- Note the D here\n            local m = ngx.re.match(target, regex, 'D')\n\n            ngx.say(type(m.group1))\n            ngx.say(type(m.group2))\n        \";\n    }\n--- request\nGET /t\n--- response_body\nnil\nnil\n--- no_error_log\n[error]\n\n\n\n=== TEST 42: bad UTF-8\n--- config\n    location = /t {\n        content_by_lua '\n            local target = \"你好\"\n            local regex = \"你好\"\n\n            -- Note the D here\n            local m, err = ngx.re.match(string.sub(target, 1, 4), regex, \"u\")\n\n            if err then\n                ngx.say(\"error: \", err)\n                return\n            end\n\n            if m then\n                ngx.say(\"matched: \", m[0])\n            else\n                ngx.say(\"not matched\")\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre_exec\\(\\) failed: -4\\n\"\n:\n\"error: pcre_exec\\(\\) failed: -10\\n\"\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 43: UTF-8 mode without UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"你好\", \".\", \"U\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 2000\n\n--- request\n    GET /re\n--- response_body\n你\n--- no_error_log\n[error]\n\n\n\n=== TEST 44: UTF-8 mode with UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"你好\", \".\", \"u\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 0\n\n--- request\n    GET /re\n--- response_body\n你\n--- no_error_log\n[error]\n\n\n\n=== TEST 45: just hit match limit\n--- http_config\n    lua_regex_match_limit 5000;\n--- config\n    location /re {\n        content_by_lua_file html/a.lua;\n    }\n\n--- user_files\n>>> a.lua\nlocal re = [==[(?i:([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:=|<=>|r?like|sounds\\s+like|regexp)([\\s'\\\"`´’‘\\(\\)]*)?\\2|([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:!=|<=|>=|<>|<|>|\\^|is\\s+not|not\\s+like|not\\s+regexp)([\\s'\\\"`´’‘\\(\\)]*)?(?!\\6)([\\d\\w]+))]==]\n\nlocal s = string.rep([[ABCDEFG]], 10)\n\nlocal start = ngx.now()\n\nlocal res, err = ngx.re.match(s, re, \"o\")\n\n--[[\nngx.update_time()\nlocal elapsed = ngx.now() - start\nngx.say(elapsed, \" sec elapsed.\")\n]]\n\nif not res then\n    if err then\n        ngx.say(\"error: \", err)\n        return\n    end\n    ngx.say(\"failed to match\")\n    return\nend\n\n--- request\n    GET /re\n--- response_body eval\n# lua_regex_match_limit uses pcre_extra->match_limit in the PCRE,\n# but PCRE2 replaces this with pcre2_set_match_limit interface,\n# which has different effects.\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"failed to match\\n\"\n:\n\"error: pcre_exec() failed: -8\\n\"\n\n\n\n=== TEST 46: just not hit match limit\n--- http_config\n    lua_regex_match_limit 5100;\n--- config\n    location /re {\n        content_by_lua_file html/a.lua;\n    }\n\n--- user_files\n>>> a.lua\nlocal re = [==[(?i:([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:=|<=>|r?like|sounds\\s+like|regexp)([\\s'\\\"`´’‘\\(\\)]*)?\\2|([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:!=|<=|>=|<>|<|>|\\^|is\\s+not|not\\s+like|not\\s+regexp)([\\s'\\\"`´’‘\\(\\)]*)?(?!\\6)([\\d\\w]+))]==]\n\nlocal s = string.rep([[ABCDEFG]], 10)\n\nlocal start = ngx.now()\n\nlocal res, err = ngx.re.match(s, re, \"o\")\n\n--[[\nngx.update_time()\nlocal elapsed = ngx.now() - start\nngx.say(elapsed, \" sec elapsed.\")\n]]\n\nif not res then\n    if err then\n        ngx.say(\"error: \", err)\n        return\n    end\n    ngx.say(\"failed to match\")\n    return\nend\n\n--- request\n    GET /re\n--- response_body\nfailed to match\n\n\n\n=== TEST 47: extra table argument\n--- config\n    location /re {\n        content_by_lua '\n            local res = {}\n            local s = \"hello, 1234\"\n            local m = ngx.re.match(s, [[(\\\\d)(\\\\d)]], \"o\", nil, res)\n            if m then\n                ngx.say(\"1: m size: \", #m)\n                ngx.say(\"1: res size: \", #res)\n            else\n                ngx.say(\"1: not matched!\")\n            end\n            m = ngx.re.match(s, [[(\\\\d)]], \"o\", nil, res)\n            if m then\n                ngx.say(\"2: m size: \", #m)\n                ngx.say(\"2: res size: \", #res)\n            else\n                ngx.say(\"2: not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1: m size: 2\n1: res size: 2\n2: m size: 2\n2: res size: 2\n--- no_error_log\n[error]\n\n\n\n=== TEST 48: init_by_lua_block\n--- http_config\n    init_by_lua_block {\n        local m, err = ngx.re.match(\"hello, 1234\", [[(\\d+)]])\n        if not m then\n            ngx.log(ngx.ERR, \"failed to match: \", err)\n        else\n            package.loaded.m = m\n        end\n    }\n--- config\n    location /re {\n        content_by_lua_block {\n            local m = package.loaded.m\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        }\n    }\n--- request\n    GET /re\n--- response_body\n1234\n--- no_error_log\n[error]\n\n\n\n=== TEST 49: trailing captures are false\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\", \"(hello)(.+)?\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nhello\nfalse\n\n\n\n=== TEST 50: the 5th argument hides the 4th (GitHub #719)\n--- config\n    location /re {\n        content_by_lua '\n            local ctx, m = { pos = 5 }, {};\n            local _, err = ngx.re.match(\"20172016-11-3 03:07:09\", [=[(\\d\\d\\d\\d)]=], \"\", ctx, m);\n            if m then\n                ngx.say(m[0], \" \", _[0], \" \", ctx.pos)\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n2016 2016 9\n"
  },
  {
    "path": "t/035-gmatch.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(5);\n\nplan tests => repeat_each() * (blocks() * 2 + 7);\n\nour $HtmlDir = html_dir;\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: gmatch\n--- config\n    location /re {\n        content_by_lua '\n            for m in ngx.re.gmatch(\"hello, world\", \"[a-z]+\") do\n                if m then\n                    ngx.say(m[0])\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nworld\n\n\n\n=== TEST 2: fail to match\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[0-9]\")\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnil\nnil\nnil\n\n\n\n=== TEST 3: match but iterate more times (not just match at the end)\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world!\", \"[a-z]+\")\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nworld\nnil\nnil\n\n\n\n=== TEST 4: match but iterate more times (just matched at the end)\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[a-z]+\")\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nworld\nnil\nnil\n\n\n\n=== TEST 5: anchored match (failed)\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, 1234\", \"([0-9]+)\", \"a\")\n            ngx.say(it())\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnil\n\n\n\n=== TEST 6: anchored match (succeeded)\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"12 hello 34\", \"[0-9]\", \"a\")\n            local m = it()\n            ngx.say(m[0])\n            m = it()\n            ngx.say(m[0])\n            ngx.say(it())\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1\n2\nnil\n\n\n\n=== TEST 7: non-anchored gmatch (without regex cache)\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"12 hello 34\", \"[0-9]\")\n            local m = it()\n            ngx.say(m and m[0])\n            m = it()\n            ngx.say(m and m[0])\n            m = it()\n            ngx.say(m and m[0])\n            m = it()\n            ngx.say(m and m[0])\n            m = it()\n            ngx.say(m and m[0])\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1\n2\n3\n4\nnil\n\n\n\n=== TEST 8: non-anchored gmatch (with regex cache)\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"12 hello 34\", \"[0-9]\", \"o\")\n            local m = it()\n            ngx.say(m and m[0])\n            m = it()\n            ngx.say(m and m[0])\n            m = it()\n            ngx.say(m and m[0])\n            m = it()\n            ngx.say(m and m[0])\n            m = it()\n            ngx.say(m and m[0])\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1\n2\n3\n4\nnil\n\n\n\n=== TEST 9: anchored match (succeeded)\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"12 hello 34\", \"[0-9]\", \"a\")\n            local m = it()\n            ngx.say(m[0])\n            m = it()\n            ngx.say(m[0])\n            ngx.say(it())\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1\n2\nnil\n\n\n\n=== TEST 10: anchored match (succeeded, set_by_lua)\n--- config\n    location /re {\n        set_by_lua $res '\n            local it = ngx.re.gmatch(\"12 hello 34\", \"[0-9]\", \"a\")\n            local m = it()\n            return m[0]\n        ';\n        echo $res;\n    }\n--- request\n    GET /re\n--- response_body\n1\n\n\n\n=== TEST 11: gmatch (look-behind assertion)\n--- config\n    location /re {\n        content_by_lua '\n            for m in ngx.re.gmatch(\"{foobar}, {foobaz}\", \"(?<=foo)ba[rz]\") do\n                if m then\n                    ngx.say(m[0])\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nbar\nbaz\n\n\n\n=== TEST 12: gmatch (look-behind assertion 2)\n--- config\n    location /re {\n        content_by_lua '\n            for m in ngx.re.gmatch(\"{foobarbaz}\", \"(?<=foo)bar|(?<=bar)baz\") do\n                if m then\n                    ngx.say(m[0])\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nbar\nbaz\n\n\n\n=== TEST 13: with regex cache\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, 1234\", \"([A-Z]+)\", \"io\")\n            local m = it()\n            ngx.say(m and m[0])\n\n            it = ngx.re.gmatch(\"1234, okay\", \"([A-Z]+)\", \"io\")\n            m = it()\n            ngx.say(m and m[0])\n\n            it = ngx.re.gmatch(\"hi, 1234\", \"([A-Z]+)\", \"o\")\n            m = it()\n            ngx.say(m and m[0])\n        ';\n    }\n--- request\n    GET /re\n--- stap2\nF(ngx_http_lua_ngx_re_gmatch_iterator) { println(\"iterator\") }\nF(ngx_http_lua_ngx_re_gmatch_gc) { println(\"gc\") }\nF(ngx_http_lua_ngx_re_gmatch_cleanup) { println(\"cleanup\") }\n--- response_body\nhello\nokay\nnil\n\n\n\n=== TEST 14: exceeding regex cache max entries\n--- http_config\n    lua_regex_cache_max_entries 2;\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, 1234\", \"([0-9]+)\", \"o\")\n            local m = it()\n            ngx.say(m and m[0])\n\n            it = ngx.re.gmatch(\"howdy, 567\", \"([0-9]+)\", \"oi\")\n            m = it()\n            ngx.say(m and m[0])\n\n            it = ngx.re.gmatch(\"hiya, 98\", \"([0-9]+)\", \"ox\")\n            m = it()\n            ngx.say(m and m[0])\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n567\n98\n\n\n\n=== TEST 15: disable regex cache completely\n--- http_config\n    lua_regex_cache_max_entries 0;\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, 1234\", \"([0-9]+)\", \"o\")\n            local m = it()\n            ngx.say(m and m[0])\n\n            it = ngx.re.gmatch(\"howdy, 567\", \"([0-9]+)\", \"oi\")\n            local m = it()\n            ngx.say(m and m[0])\n\n            it = ngx.re.gmatch(\"hiya, 98\", \"([0-9]+)\", \"ox\")\n            local m = it()\n            ngx.say(m and m[0])\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n567\n98\n\n\n\n=== TEST 16: gmatch matched but no iterate\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[a-z]+\")\n            ngx.say(\"done\")\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ndone\n\n\n\n=== TEST 17: gmatch matched but only iterate once and still matches remain\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[a-z]+\")\n            local m = it()\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\n\n\n\n=== TEST 18: gmatch matched but no iterate and early forced GC\n--- config\n    location /re {\n        content_by_lua '\n            local a = {}\n            for i = 1, 3 do\n                local it = ngx.re.gmatch(\"hello, world\", \"[a-z]+\")\n                it()\n                collectgarbage()\n                table.insert(a, {\"hello\", \"world\"})\n            end\n            ngx.say(\"done\")\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ndone\n\n\n\n=== TEST 19: gmatch iterator used by another request\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;;';\"\n--- config\n    location /main {\n        content_by_lua '\n            package.loaded.foo = nil\n            collectgarbage()\n\n            local res = ngx.location.capture(\"/t\")\n            if res.status == 200 then\n                ngx.print(res.body)\n            else\n                ngx.say(\"sr failed: \", res.status)\n            end\n\n            res = ngx.location.capture(\"/t\")\n            if res.status == 200 then\n                ngx.print(res.body)\n            else\n                ngx.say(\"sr failed: \", res.status)\n            end\n        ';\n    }\n\n    location /t {\n        content_by_lua '\n            local foo = require \"foo\"\n            local m = foo.go()\n            ngx.say(m and \"matched\" or \"no\")\n        ';\n    }\n--- user_files\n>>> foo.lua\nmodule(\"foo\", package.seeall)\n\nlocal it\n\nfunction go()\n    if not it then\n        it = ngx.re.gmatch(\"hello, world\", \"[a-z]+\")\n    end\n\n    return it()\nend\n--- request\n    GET /main\n--- response_body\nmatched\nmatched\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: gmatch (empty matched string)\n--- config\n    location /re {\n        content_by_lua '\n            for m in ngx.re.gmatch(\"hello\", \"a|\") do\n                if m then\n                    ngx.say(\"matched: [\", m[0], \"]\")\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nmatched: []\nmatched: []\nmatched: []\nmatched: []\nmatched: []\nmatched: []\n\n\n\n=== TEST 21: gmatch with named pattern\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"1234, 1234\", \"(?<first>[0-9]+)\")\n            local m = it()\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[\"first\"])\n            else\n                ngx.say(\"not matched!\")\n            end\n\n            m = it()\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[\"first\"])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n1234\n1234\n1234\n1234\n1234\n\n\n\n=== TEST 22: gmatch with multiple named pattern\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"1234, abcd, 1234\", \"(?<first>[0-9]+)|(?<second>[a-z]+)\")\n\n            local m = it()\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n                ngx.say(m[\"first\"])\n                ngx.say(m[\"second\"])\n            else\n                ngx.say(\"not matched!\")\n            end\n\n            m = it()\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n                ngx.say(m[\"first\"])\n                ngx.say(m[\"second\"])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n1234\nfalse\n1234\nfalse\nabcd\nfalse\nabcd\nfalse\nabcd\n\n\n\n=== TEST 23: gmatch with duplicate named pattern w/ extraction\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, 1234\", \"(?<first>[a-z]+), (?<first>[0-9]+)\", \"D\")\n            local m = it()\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n                ngx.say(table.concat(m.first,\"-\"))\n            else\n                ngx.say(\"not matched!\")\n            end\n\n            m = it()\n            if m then\n             ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n                ngx.say(table.concat(m.first,\"-\"))\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, 1234\nhello\n1234\nhello-1234\nnot matched!\n\n\n\n=== TEST 24: named captures are empty\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"1234\", \"(?<first>[a-z]*)([0-9]+)\", \"\")\n            local m = it()\n            if m then\n                ngx.say(m[0])\n                ngx.say(m.first)\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n1234\n\n\n\n=== TEST 25: named captures are empty (with regex cache)\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"1234\", \"(?<first>[a-z]*)([0-9]+)\", \"o\")\n            local m = it()\n            if m then\n                ngx.say(m[0])\n                ngx.say(m.first)\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n1234\n\n\n\n=== TEST 26: bad pattern\n--- config\n    location /re {\n        content_by_lua '\n            local it, err = ngx.re.gmatch(\"hello\\\\nworld\", \"(abc\")\n            if not err then\n                ngx.say(\"good\")\n\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 27: bad UTF-8\n--- config\n    location = /t {\n        content_by_lua '\n            local target = \"你好\"\n            local regex = \"你好\"\n\n            -- Note the D here\n            local it, err = ngx.re.gmatch(string.sub(target, 1, 4), regex, \"u\")\n\n            if err then\n                ngx.say(\"error: \", err)\n                return\n            end\n\n            local m, err = it()\n            if err then\n                ngx.say(\"error: \", err)\n                return\n            end\n\n            if m then\n                ngx.say(\"matched: \", m[0])\n            else\n                ngx.say(\"not matched\")\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre_exec\\(\\) failed: -4\\n\"\n:\n\"error: pcre_exec\\(\\) failed: -10\\n\"\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 28: UTF-8 mode without UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"你好\", \".\", \"U\")\n            local m = it()\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 2000\n\n--- request\n    GET /re\n--- response_body\n你\n--- no_error_log\n[error]\n\n\n\n=== TEST 29: UTF-8 mode with UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"你好\", \".\", \"u\")\n            local m = it()\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 0\n\n--- request\n    GET /re\n--- response_body\n你\n--- no_error_log\n[error]\n\n\n\n=== TEST 30: just hit match limit\n--- http_config\n    lua_regex_match_limit 5000;\n--- config\n    location /re {\n        content_by_lua_file html/a.lua;\n    }\n\n--- user_files\n>>> a.lua\nlocal re = [==[(?i:([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:=|<=>|r?like|sounds\\s+like|regexp)([\\s'\\\"`´’‘\\(\\)]*)?\\2|([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:!=|<=|>=|<>|<|>|\\^|is\\s+not|not\\s+like|not\\s+regexp)([\\s'\\\"`´’‘\\(\\)]*)?(?!\\6)([\\d\\w]+))]==]\n\nlocal s = string.rep([[ABCDEFG]], 10)\n\nlocal start = ngx.now()\n\nlocal it, err = ngx.re.gmatch(s, re, \"o\")\nif not it then\n    ngx.say(\"failed to gen iterator: \", err)\n    return\nend\n\nlocal res, err = it()\n\n--[[\nngx.update_time()\nlocal elapsed = ngx.now() - start\nngx.say(elapsed, \" sec elapsed.\")\n]]\n\nif not res then\n    if err then\n        ngx.say(\"error: \", err)\n        return\n    end\n    ngx.say(\"failed to match\")\n    return\nend\n\n--- request\n    GET /re\n--- response_body eval\n# lua_regex_match_limit uses pcre_extra->match_limit in the PCRE,\n# but PCRE2 replaces this with pcre2_set_match_limit interface,\n# which has different effects.\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"failed to match\\n\"\n:\n\"error: pcre_exec() failed: -8\\n\"\n\n\n\n=== TEST 31: just not hit match limit\n--- http_config\n    lua_regex_match_limit 5100;\n--- config\n    location /re {\n        content_by_lua_file html/a.lua;\n    }\n\n--- user_files\n>>> a.lua\nlocal re = [==[(?i:([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:=|<=>|r?like|sounds\\s+like|regexp)([\\s'\\\"`´’‘\\(\\)]*)?\\2|([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:!=|<=|>=|<>|<|>|\\^|is\\s+not|not\\s+like|not\\s+regexp)([\\s'\\\"`´’‘\\(\\)]*)?(?!\\6)([\\d\\w]+))]==]\n\nlocal s = string.rep([[ABCDEFG]], 10)\n\nlocal start = ngx.now()\n\nlocal it, err = ngx.re.gmatch(s, re, \"o\")\nif not it then\n    ngx.say(\"failed to gen iterator: \", err)\n    return\nend\n\nlocal res, err = it()\n\n--[[\nngx.update_time()\nlocal elapsed = ngx.now() - start\nngx.say(elapsed, \" sec elapsed.\")\n]]\n\nif not res then\n    if err then\n        ngx.say(\"error: \", err)\n        return\n    end\n    ngx.say(\"failed to match\")\n    return\nend\n\n--- request\n    GET /re\n--- response_body\nfailed to match\n"
  },
  {
    "path": "t/036-sub.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 19);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: matched but w/o variables\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, world\", \"[a-z]+\", \"howdy\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhowdy, world\n1\n\n\n\n=== TEST 2: not matched\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, world\", \"[A-Z]+\", \"howdy\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world\n0\n\n\n\n=== TEST 3: matched and with variables\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"a b c d\", \"(b) (c)\", \"[$0] [$1] [$2] [$3] [$134]\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\na [b c] [b] [c] [] [] d\n1\n\n\n\n=== TEST 4: matched and with named variables\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"a b c d\",\n                \"(b) (c)\", \"[$0] [$1] [$2] [$3] [$hello]\")\n            if s then\n                ngx.say(s, \": \", n)\n\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: failed to compile the replacement template\n--- error_log\nattempt to use named capturing variable \"hello\" (named captures not supported yet)\n\n\n\n=== TEST 5: matched and with named variables (bracketed)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"a b c d\",\n                \"(b) (c)\", \"[$0] [$1] [$2] [$3] [${hello}]\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: failed to compile the replacement template\n--- error_log\nattempt to use named capturing variable \"hello\" (named captures not supported yet)\n\n\n\n=== TEST 6: matched and with bracketed variables\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"b c d\", \"(b) (c)\", \"[$0] [$1] [${2}] [$3] [${134}]\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n[b c] [b] [c] [] [] d\n1\n\n\n\n=== TEST 7: matched and with bracketed variables (unmatched brackets)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"b c d\", \"(b) (c)\", \"[$0] [$1] [${2}] [$3] [${134]\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: failed to compile the replacement template\n--- error_log\nthe closing bracket in \"134\" variable is missing\n\n\n\n=== TEST 8: matched and with bracketed variables (unmatched brackets)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"b c d\", \"(b) (c)\", \"[$0] [$1] [${2}] [$3] [${134\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: failed to compile the replacement template\n--- error_log\nthe closing bracket in \"134\" variable is missing\n\n\n\n=== TEST 9: matched and with bracketed variables (unmatched brackets)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"b c d\", \"(b) (c)\", \"[$0] [$1] [${2}] [$3] [${\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: failed to compile the replacement template\n--- error_log\nlua script: invalid capturing variable name found in \"[$0] [$1] [${2}] [$3] [${\"\n\n\n\n=== TEST 10: trailing $\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"b c d\", \"(b) (c)\", \"[$0] [$1] [${2}] [$3] [$\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: failed to compile the replacement template\n--- error_log\nlua script: invalid capturing variable name found in \"[$0] [$1] [${2}] [$3] [$\"\n\n\n\n=== TEST 11: matched but w/o variables and with literal $\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, world\", \"[a-z]+\", \"ho$$wdy\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nho$wdy, world\n1\n\n\n\n=== TEST 12: non-anchored match\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, 1234\", \"[0-9]\", \"x\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, x234\n1\n\n\n\n=== TEST 13: anchored match\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, 1234\", \"[0-9]\", \"x\", \"a\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, 1234\n0\n\n\n\n=== TEST 14: function replace\n--- config\n    location /re {\n        content_by_lua '\n            local repl = function (m)\n                return \"[\" .. m[0] .. \"] [\" .. m[1] .. \"]\"\n            end\n\n            local s, n = ngx.re.sub(\"hello, 34\", \"([0-9])\", repl)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, [3] [3]4\n1\n\n\n\n=== TEST 15: function replace (failed)\n--- config\n    location /re {\n        content_by_lua '\n            local repl = function (m)\n                return \"[\" .. m[0] .. \"] [\" .. m[1] .. \"]\"\n            end\n\n            local s, n = ngx.re.sub(\"hello, 34\", \"([A-Z])\", repl)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, 34\n0\n\n\n\n=== TEST 16: bad repl arg type\n--- config\n    location /re {\n        content_by_lua '\n            local rc, s, n = pcall(ngx.re.sub, \"hello, 34\", \"([A-Z])\", true)\n            ngx.say(rc)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfalse\nbad argument #3 to '?' (string, number, or function expected, got boolean)\nnil\n--- SKIP\n\n\n\n=== TEST 17: use number to replace\n--- config\n    location /re {\n        content_by_lua '\n            local rc, s, n = pcall(ngx.re.sub, \"hello, 34\", \"([0-9])\", 72)\n            ngx.say(rc)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ntrue\nhello, 724\n1\n\n\n\n=== TEST 18: bad function return value type\n--- SKIP\n--- config\n    location /re {\n        content_by_lua '\n            local f = function (m) end\n            local rc, s, n = pcall(ngx.re.sub, \"hello, 34\", \"([0-9])\", f)\n            ngx.say(rc)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfalse\nbad argument #3 to '?' (string or number expected to be returned by the replace function, got nil)\nnil\n\n\n\n=== TEST 19: matched but w/o variables (set_by_lua)\n--- config\n    location /re {\n        set_by_lua $res '\n            local s, n = ngx.re.sub(\"hello, world\", \"[a-z]+\", \"howdy\")\n            return s\n        ';\n        echo $res;\n    }\n--- request\n    GET /re\n--- response_body\nhowdy, world\n\n\n\n=== TEST 20: matched and with variables w/o using named patterns in sub\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"a b c d\", \"(?<first>b) (?<second>c)\", \"[$0] [$1] [$2] [$3] [$134]\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\na [b c] [b] [c] [] [] d\n1\n\n\n\n=== TEST 21: matched and with variables using named patterns in func\n--- config\n    error_log /tmp/nginx_error debug;\n    location /re {\n        content_by_lua '\n            local repl = function (m)\n                return \"[\" .. m[0] .. \"] [\" .. m[\"first\"] .. \"] [\" .. m[2] .. \"]\"\n            end\n\n            local s, n = ngx.re.sub(\"a b c d\", \"(?<first>b) (?<second>c)\", repl)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\na [b c] [b] [c] d\n1\n--- no_error_log\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 22: matched and with variables w/ using named patterns in sub\nThis is still a TODO\n--- SKIP\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"a b c d\", \"(?<first>b) (?<second>c)\", \"[$0] [${first}] [${second}] [$3] [$134]\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\na [b c] [b] [c] [] [] d\n1\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: $0 without parens\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"a b c d\", [[\\w]], \"[$0]\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n[a] b c d\n1\n--- no_error_log\n[error]\n\n\n\n=== TEST 24: bad pattern\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"hello\\\\nworld\", \"(abc\", \"\")\n            if s then\n                ngx.say(\"subs: \", n)\n\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 25: bad UTF-8\n--- config\n    location = /t {\n        content_by_lua '\n            local target = \"你好\"\n            local regex = \"你好\"\n\n            -- Note the D here\n            local s, n, err = ngx.re.sub(string.sub(target, 1, 4), regex, \"\", \"u\")\n\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre_exec\\(\\) failed: -4\\n\"\n:\n\"error: pcre_exec\\(\\) failed: -10\\n\"\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 26: UTF-8 mode without UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"你好\", \".\", \"a\", \"U\")\n            if s then\n                ngx.say(\"s: \", s)\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 2000\n\n--- request\n    GET /re\n--- response_body\ns: a好\n--- no_error_log\n[error]\n\n\n\n=== TEST 27: UTF-8 mode with UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"你好\", \".\", \"a\", \"u\")\n            if s then\n                ngx.say(\"s: \", s)\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 0\n\n--- request\n    GET /re\n--- response_body\ns: a好\n--- no_error_log\n[error]\n\n\n\n=== TEST 28: just hit match limit\n--- http_config\n    lua_regex_match_limit 5000;\n--- config\n    location /re {\n        content_by_lua_file html/a.lua;\n    }\n\n--- user_files\n>>> a.lua\nlocal re = [==[(?i:([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:=|<=>|r?like|sounds\\s+like|regexp)([\\s'\\\"`´’‘\\(\\)]*)?\\2|([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:!=|<=|>=|<>|<|>|\\^|is\\s+not|not\\s+like|not\\s+regexp)([\\s'\\\"`´’‘\\(\\)]*)?(?!\\6)([\\d\\w]+))]==]\n\nlocal s = string.rep([[ABCDEFG]], 10)\n\nlocal start = ngx.now()\n\nlocal res, cnt, err = ngx.re.sub(s, re, \"\", \"o\")\n\n--[[\nngx.update_time()\nlocal elapsed = ngx.now() - start\nngx.say(elapsed, \" sec elapsed.\")\n]]\n\nif err then\n    ngx.say(\"error: \", err)\n    return\nend\nngx.say(\"sub: \", cnt)\n\n--- request\n    GET /re\n--- response_body eval\n# lua_regex_match_limit uses pcre_extra->match_limit in the PCRE,\n# but PCRE2 replaces this with pcre2_set_match_limit interface,\n# which has different effects.\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"sub: 0\\n\"\n:\n\"error: pcre_exec() failed: -8\\n\"\n\n\n\n=== TEST 29: just not hit match limit\n--- http_config\n    lua_regex_match_limit 5100;\n--- config\n    location /re {\n        content_by_lua_file html/a.lua;\n    }\n\n--- user_files\n>>> a.lua\nlocal re = [==[(?i:([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:=|<=>|r?like|sounds\\s+like|regexp)([\\s'\\\"`´’‘\\(\\)]*)?\\2|([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:!=|<=|>=|<>|<|>|\\^|is\\s+not|not\\s+like|not\\s+regexp)([\\s'\\\"`´’‘\\(\\)]*)?(?!\\6)([\\d\\w]+))]==]\n\nlocal s = string.rep([[ABCDEFG]], 10)\n\nlocal start = ngx.now()\n\nlocal res, cnt, err = ngx.re.sub(s, re, \"\", \"o\")\n\n--[[\nngx.update_time()\nlocal elapsed = ngx.now() - start\nngx.say(elapsed, \" sec elapsed.\")\n]]\n\nif err then\n    ngx.say(\"error: \", err)\n    return\nend\nngx.say(\"sub: \", cnt)\n\n--- request\n    GET /re\n--- response_body\nsub: 0\n\n\n\n=== TEST 30: bug: sub incorrectly swallowed a character is the first character\nOriginal bad result: estCase\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"TestCase\", \"^ *\", \"\", \"o\")\n            if s then\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nTestCase\n\n\n\n=== TEST 31: bug: sub incorrectly swallowed a character is not the first character\nOriginal bad result: .b.d\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"abcd\", \"(?=c)\", \".\")\n            if s then\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nab.cd\n\n\n\n=== TEST 32: ngx.re.gsub: recursive calling (github #445)\n--- config\n\nlocation = /t {\n    content_by_lua '\n        local function test()\n            local data = [[\n                OUTER {FIRST}\n]]\n\n            local p1 = \"(OUTER)(.+)\"\n            local p2 = \"{([A-Z]+)}\"\n\n            ngx.print(data)\n\n            local res =  ngx.re.gsub(data, p1, function(m)\n                -- ngx.say(\"pre: m[1]: [\", m[1], \"]\")\n                -- ngx.say(\"pre: m[2]: [\", m[2], \"]\")\n\n                local res = ngx.re.gsub(m[2], p2, function(_)\n                    return \"REPLACED\"\n                end, \"\")\n\n                -- ngx.say(\"post: m[1]: [\", m[1], \"]\")\n                -- ngx.say(\"post m[2]: [\", m[2], \"]\")\n                return m[1] .. res\n            end, \"\")\n\n            ngx.print(res)\n        end\n\n        test()\n    ';\n}\n--- request\nGET /t\n--- response_body\n                OUTER {FIRST}\n                OUTER REPLACED\n--- no_error_log\n[error]\nbad argument type\nNYI\n\n\n\n=== TEST 33: function replace (false for groups)\n--- config\n    location /re {\n        content_by_lua '\n            local repl = function (m)\n                print(\"group 1: \", m[2])\n                return \"[\" .. m[0] .. \"] [\" .. m[1] .. \"]\"\n            end\n\n            local s, n = ngx.re.sub(\"hello, 34\", \"([0-9])|(world)\", repl)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, [3] [3]4\n1\n--- error_log\ngroup 1: false\n"
  },
  {
    "path": "t/037-gsub.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 17);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"[hello, world]\", \"[a-z]+\", \"howdy\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n[howdy, howdy]\n2\n\n\n\n=== TEST 2: trimmed\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, world\", \"[a-z]+\", \"howdy\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhowdy, howdy\n2\n\n\n\n=== TEST 3: not matched\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, world\", \"[A-Z]+\", \"howdy\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world\n0\n\n\n\n=== TEST 4: replace by function (trimmed)\n--- config\n    location /re {\n        content_by_lua '\n            local f = function (m)\n                return \"[\" .. m[0] .. \",\" .. m[1] .. \"]\"\n            end\n\n            local s, n = ngx.re.gsub(\"hello, world\", \"([a-z])[a-z]+\", f)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n[hello,h], [world,w]\n2\n\n\n\n=== TEST 5: replace by function (not trimmed)\n--- config\n    location /re {\n        content_by_lua '\n            local f = function (m)\n                return \"[\" .. m[0] .. \",\" .. m[1] .. \"]\"\n            end\n\n            local s, n = ngx.re.gsub(\"{hello, world}\", \"([a-z])[a-z]+\", f)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n{[hello,h], [world,w]}\n2\n\n\n\n=== TEST 6: replace by script (trimmed)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, world\", \"([a-z])[a-z]+\", \"[$0,$1]\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n[hello,h], [world,w]\n2\n\n\n\n=== TEST 7: replace by script (not trimmed)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"{hello, world}\", \"([a-z])[a-z]+\", \"[$0,$1]\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n{[hello,h], [world,w]}\n2\n\n\n\n=== TEST 8: set_by_lua\n--- config\n    location /re {\n        set_by_lua $res '\n            local f = function (m)\n                return \"[\" .. m[0] .. \",\" .. m[1] .. \"]\"\n            end\n\n            local s, n = ngx.re.gsub(\"{hello, world}\", \"([a-z])[a-z]+\", f)\n            return s\n        ';\n        echo $res;\n    }\n--- request\n    GET /re\n--- response_body\n{[hello,h], [world,w]}\n\n\n\n=== TEST 9: look-behind assertion\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"{foobarbaz}\", \"(?<=foo)bar|(?<=bar)baz\", \"h$0\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n{foohbarhbaz}\n2\n\n\n\n=== TEST 10: gsub with a patch matching an empty substring (string template)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello\", \"a|\", \"b\")\n            ngx.say(\"s: \", s)\n            ngx.say(\"n: \", n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ns: bhbeblblbob\nn: 6\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: gsub with a patch matching an empty substring (string template, empty subj)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"\", \"a|\", \"b\")\n            ngx.say(\"s: \", s)\n            ngx.say(\"n: \", n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ns: b\nn: 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: gsub with a patch matching an empty substring (func)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello\", \"a|\", function () return \"b\" end)\n            ngx.say(\"s: \", s)\n            ngx.say(\"n: \", n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ns: bhbeblblbob\nn: 6\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: gsub with a patch matching an empty substring (func, empty subj)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"\", \"a|\", function () return \"b\" end)\n            ngx.say(\"s: \", s)\n            ngx.say(\"n: \", n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ns: b\nn: 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: big subject string exceeding the luabuf chunk size (with trailing unmatched data, func repl)\n--- config\n    location /re {\n        content_by_lua '\n            local subj = string.rep(\"a\", 8000)\n                .. string.rep(\"b\", 1000)\n                .. string.rep(\"a\", 8000)\n                .. string.rep(\"b\", 1000)\n                .. \"aaa\"\n\n            local function repl(m)\n                return string.rep(\"c\", string.len(m[0]))\n            end\n\n            local s, n = ngx.re.gsub(subj, \"b+\", repl)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n(\"a\" x 8000) . (\"c\" x 1000) . (\"a\" x 8000) . (\"c\" x 1000)\n. \"aaa\n2\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: big subject string exceeding the luabuf chunk size (without trailing unmatched data, func repl)\n--- config\n    location /re {\n        content_by_lua '\n            local subj = string.rep(\"a\", 8000)\n                .. string.rep(\"b\", 1000)\n                .. string.rep(\"a\", 8000)\n                .. string.rep(\"b\", 1000)\n\n            local function repl(m)\n                return string.rep(\"c\", string.len(m[0]))\n            end\n\n            local s, n = ngx.re.gsub(subj, \"b+\", repl)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n(\"a\" x 8000) . (\"c\" x 1000) . (\"a\" x 8000) . (\"c\" x 1000)\n. \"\\n2\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: big subject string exceeding the luabuf chunk size (with trailing unmatched data, str repl)\n--- config\n    location /re {\n        content_by_lua '\n            local subj = string.rep(\"a\", 8000)\n                .. string.rep(\"b\", 1000)\n                .. string.rep(\"a\", 8000)\n                .. string.rep(\"b\", 1000)\n                .. \"aaa\"\n\n            local s, n = ngx.re.gsub(subj, \"b(b+)(b)\", \"$1 $2\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n(\"a\" x 8000) . (\"b\" x 998) . \" b\" . (\"a\" x 8000) . (\"b\" x 998) . \" baaa\n2\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: big subject string exceeding the luabuf chunk size (without trailing unmatched data, str repl)\n--- config\n    location /re {\n        content_by_lua '\n            local subj = string.rep(\"a\", 8000)\n                .. string.rep(\"b\", 1000)\n                .. string.rep(\"a\", 8000)\n                .. string.rep(\"b\", 1000)\n\n            local s, n = ngx.re.gsub(subj, \"b(b+)(b)\", \"$1 $2\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n(\"a\" x 8000) . (\"b\" x 998) . \" b\" . (\"a\" x 8000) . (\"b\" x 998) . \" b\\n2\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: named pattern repl w/ callback\n--- config\n    location /re {\n       content_by_lua '\n            local repl = function (m)\n                return \"[\" .. m[0] .. \",\" .. m[\"first\"] .. \"]\"\n            end\n\n            local s, n = ngx.re.gsub(\"hello, world\", \"(?<first>[a-z])[a-z]+\", repl)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n[hello,h], [world,w]\n2\n\n\n\n=== TEST 19: $0 without parens\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"a b c d\", [[\\w]], \"[$0]\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n[a] [b] [c] [d]\n4\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: bad UTF-8\n--- config\n    location = /t {\n        content_by_lua '\n            local target = \"你好\"\n            local regex = \"你好\"\n\n            -- Note the D here\n            local s, n, err = ngx.re.gsub(string.sub(target, 1, 4), regex, \"\", \"u\")\n\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n       ';\n    }\n--- request\nGET /t\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre_exec\\(\\) failed: -4\\n\"\n:\n\"error: pcre_exec\\(\\) failed: -10\\n\"\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: UTF-8 mode without UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.gsub(\"你好\", \".\", \"a\", \"U\")\n            if s then\n                ngx.say(\"s: \", s)\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 2000\nexec opts: 2000\nexec opts: 2000\n\n--- request\n    GET /re\n--- response_body\ns: aa\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: UTF-8 mode with UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.gsub(\"你好\", \".\", \"a\", \"u\")\n            if s then\n                ngx.say(\"s: \", s)\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 0\nexec opts: 0\nexec opts: 0\n\n--- request\n    GET /re\n--- response_body\ns: aa\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: just hit match limit\n--- http_config\n    lua_regex_match_limit 5000;\n--- config\n    location /re {\n        content_by_lua_file html/a.lua;\n    }\n\n--- user_files\n>>> a.lua\nlocal re = [==[(?i:([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:=|<=>|r?like|sounds\\s+like|regexp)([\\s'\\\"`´’‘\\(\\)]*)?\\2|([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:!=|<=|>=|<>|<|>|\\^|is\\s+not|not\\s+like|not\\s+regexp)([\\s'\\\"`´’‘\\(\\)]*)?(?!\\6)([\\d\\w]+))]==]\n\nlocal s = string.rep([[ABCDEFG]], 10)\n\nlocal start = ngx.now()\n\nlocal res, cnt, err = ngx.re.gsub(s, re, \"\", \"o\")\n\n--[[\nngx.update_time()\nlocal elapsed = ngx.now() - start\nngx.say(elapsed, \" sec elapsed.\")\n]]\n\nif err then\n    ngx.say(\"error: \", err)\n    return\nend\nngx.say(\"gsub: \", cnt)\n\n--- request\n    GET /re\n--- response_body eval\n# lua_regex_match_limit uses pcre_extra->match_limit in the PCRE,\n# but PCRE2 replaces this with pcre2_set_match_limit interface,\n# which has different effects.\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"gsub: 0\\n\"\n:\n\"error: pcre_exec() failed: -8\\n\"\n\n\n\n=== TEST 24: just not hit match limit\n--- http_config\n    lua_regex_match_limit 5100;\n--- config\n    location /re {\n        content_by_lua_file html/a.lua;\n    }\n\n--- user_files\n>>> a.lua\nlocal re = [==[(?i:([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:=|<=>|r?like|sounds\\s+like|regexp)([\\s'\\\"`´’‘\\(\\)]*)?\\2|([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:!=|<=|>=|<>|<|>|\\^|is\\s+not|not\\s+like|not\\s+regexp)([\\s'\\\"`´’‘\\(\\)]*)?(?!\\6)([\\d\\w]+))]==]\n\nlocal s = string.rep([[ABCDEFG]], 10)\n\nlocal start = ngx.now()\n\nlocal res, cnt, err = ngx.re.gsub(s, re, \"\", \"o\")\n\n--[[\nngx.update_time()\nlocal elapsed = ngx.now() - start\nngx.say(elapsed, \" sec elapsed.\")\n]]\n\nif err then\n    ngx.say(\"error: \", err)\n    return\nend\nngx.say(\"gsub: \", cnt)\n\n--- request\n    GET /re\n--- response_body\ngsub: 0\n--- timeout: 10\n\n\n\n=== TEST 25: bug: gsub incorrectly swallowed a character is the first character\nOriginal bad result: estCase\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"TestCase\", \"^ *\", \"\", \"o\")\n            if s then\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nTestCase\n\n\n\n=== TEST 26: bug: gsub incorrectly swallowed a character is not the first character\nOriginal bad result: .b.d\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"abcd\", \"a|(?=c)\", \".\")\n            if s then\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n.b.cd\n\n\n\n=== TEST 27: use of ngx.req.get_headers in the user callback\n--- config\n\nlocation = /t {\n    content_by_lua '\n        local data = [[\n            INNER\n            INNER\n]]\n\n        -- ngx.say(data)\n\n        local res =  ngx.re.gsub(data, \"INNER\", function(inner_matches)\n            local header = ngx.req.get_headers()[\"Host\"]\n            -- local header = ngx.var[\"http_HEADER\"]\n            return \"INNER_REPLACED\"\n        end, \"s\")\n\n        ngx.print(res)\n    ';\n}\n\n--- request\nGET /t\n--- response_body\n            INNER_REPLACED\n            INNER_REPLACED\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 28: use of ngx.var in the user callback\n--- config\n\nlocation = /t {\n    content_by_lua '\n        local data = [[\n            INNER\n            INNER\n]]\n\n        -- ngx.say(data)\n\n        local res =  ngx.re.gsub(data, \"INNER\", function(inner_matches)\n            -- local header = ngx.req.get_headers()[\"Host\"]\n            local header = ngx.var[\"http_HEADER\"]\n            return \"INNER_REPLACED\"\n        end, \"s\")\n\n        ngx.print(res)\n    ';\n}\n\n--- request\nGET /t\n--- response_body\n            INNER_REPLACED\n            INNER_REPLACED\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 29: function replace (false for groups)\n--- config\n    location /re {\n        content_by_lua '\n            local repl = function (m)\n                print(\"group 1: \", m[2])\n                return \"[\" .. m[0] .. \"] [\" .. m[1] .. \"]\"\n            end\n\n            local s, n = ngx.re.gsub(\"hello, 34\", \"([0-9])|(world)\", repl)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, [3] [3][4] [4]\n2\n--- error_log\ngroup 1: false\n"
  },
  {
    "path": "t/038-match-o.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\nlog_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]+)\", \"o\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n\n=== TEST 2: escaping sequences\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"(\\\\\\\\d+)\", \"o\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n\n=== TEST 3: escaping sequences (bad)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"(\\\\d+)\", \"o\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\n[qr/invalid escape sequence near '\"\\('/]\n\n\n\n=== TEST 4: escaping sequences in [[ ... ]]\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"[[\\\\d+]]\", \"o\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\n[qr/invalid escape sequence near '\"\\[\\['/]\n\n\n\n=== TEST 5: single capture\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]{2})[0-9]+\", \"o\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n12\n\n\n\n=== TEST 6: multiple captures\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([a-z]+).*?([0-9]{2})[0-9]+\", \"o\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, 1234\nhello\n12\n\n\n\n=== TEST 7: not matched\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"foo\", \"o\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched: nil\n\n\n\n=== TEST 8: case sensitive by default\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"HELLO\", \"o\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched: nil\n\n\n\n=== TEST 9: case sensitive by default\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"HELLO\", \"oi\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\n\n\n\n=== TEST 10: UTF-8 mode\n--- config\n    location /re {\n        content_by_lua '\n            local rc, m = pcall(ngx.re.match, \"hello章亦春\", \"HELLO.{2}\", \"iou\")\n            if not rc then\n                ngx.say(\"error: \", m)\n                return\n            end\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body_like chop\nthis version of PCRE is not compiled with PCRE_UTF8 support|^hello章亦$\n\n\n\n=== TEST 11: multi-line mode (^ at line head)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\\\\nworld\", \"^world\", \"mo\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nworld\n\n\n\n=== TEST 12: multi-line mode (. does not match \\n)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\\\\nworld\", \".*\", \"om\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\n\n\n\n=== TEST 13: single-line mode (^ as normal)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\\\\nworld\", \"^world\", \"so\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched: nil\n\n\n\n=== TEST 14: single-line mode (dot all)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\\\\nworld\", \".*\", \"os\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nworld\n\n\n\n=== TEST 15: extended mode (ignore whitespaces)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\\\\nworld\", \"\\\\\\\\w     \\\\\\\\w\", \"xo\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhe\n\n\n\n=== TEST 16: bad pattern\n--- config\n    location /re {\n        content_by_lua '\n            local m, err = ngx.re.match(\"hello\\\\nworld\", \"(abc\", \"o\")\n            if m then\n                ngx.say(m[0])\n\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: bad option\n--- config\n    location /re {\n        content_by_lua '\n            local rc, m = pcall(ngx.re.match, \"hello\\\\nworld\", \".*\", \"Ho\")\n            if rc then\n                if m then\n                    ngx.say(m[0])\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            else\n                ngx.say(\"error: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body_like chop\n^error: .*?unknown flag \"H\"\n\n\n\n=== TEST 18: extended mode (ignore whitespaces)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, world\", \"(world)|(hello)\", \"xo\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched: \", m)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nfalse\nhello\n\n\n\n=== TEST 19: optional trailing captures\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]+)(h?)\", \"o\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n\"1234\n1234\n\n\"\n\n\n\n=== TEST 20: anchored match (failed)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]+)\", \"oa\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched!\n\n\n\n=== TEST 21: anchored match (succeeded)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"1234, hello\", \"([0-9]+)\", \"ao\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n\n=== TEST 22: match with ctx but no pos\n--- config\n    location /re {\n        content_by_lua '\n            local ctx = {}\n            local m = ngx.re.match(\"1234, hello\", \"([0-9]+)\", \"o\", ctx)\n            if m then\n                ngx.say(m[0])\n                ngx.say(ctx.pos)\n            else\n                ngx.say(\"not matched!\")\n                ngx.say(ctx.pos)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n5\n\n\n\n=== TEST 23: match with ctx and a pos\n--- config\n    location /re {\n        content_by_lua '\n            local ctx = { pos = 3 }\n            local m = ngx.re.match(\"1234, hello\", \"([0-9]+)\", \"o\", ctx)\n            if m then\n                ngx.say(m[0])\n                ngx.say(ctx.pos)\n            else\n                ngx.say(\"not matched!\")\n                ngx.say(ctx.pos)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n34\n5\n\n\n\n=== TEST 24: sanity (set_by_lua)\n--- config\n    location /re {\n        set_by_lua $res '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]+)\", \"o\")\n            if m then\n                return m[0]\n            else\n                return \"not matched!\"\n            end\n        ';\n        echo $res;\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n\n=== TEST 25: match (look-behind assertion)\n--- config\n    location /re {\n        content_by_lua '\n            local ctx = {}\n            local m = ngx.re.match(\"{foobarbaz}\", \"(?<=foo)bar|(?<=bar)baz\", \"o\", ctx)\n            ngx.say(m and m[0])\n\n            m = ngx.re.match(\"{foobarbaz}\", \"(?<=foo)bar|(?<=bar)baz\", \"o\", ctx)\n            ngx.say(m and m[0])\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nbar\nbaz\n\n\n\n=== TEST 26: match (with regex cache)\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([A-Z]+)\", \"io\")\n            ngx.say(m and m[0])\n\n            m = ngx.re.match(\"1234, okay\", \"([A-Z]+)\", \"io\")\n            ngx.say(m and m[0])\n\n            m = ngx.re.match(\"hello, 1234\", \"([A-Z]+)\", \"o\")\n            ngx.say(m and m[0])\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nokay\nnil\n\n\n\n=== TEST 27: match (with regex cache and ctx)\n--- config\n    location /re {\n        content_by_lua '\n            local ctx = {}\n            local m = ngx.re.match(\"hello, 1234\", \"([A-Z]+)\", \"io\", ctx)\n            ngx.say(m and m[0])\n            ngx.say(ctx.pos)\n\n            m = ngx.re.match(\"1234, okay\", \"([A-Z]+)\", \"io\", ctx)\n            ngx.say(m and m[0])\n            ngx.say(ctx.pos)\n\n            ctx.pos = 1\n            m = ngx.re.match(\"hi, 1234\", \"([A-Z]+)\", \"o\", ctx)\n            ngx.say(m and m[0])\n            ngx.say(ctx.pos)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\n6\nokay\n11\nnil\n1\n\n\n\n=== TEST 28: exceeding regex cache max entries\n--- http_config\n    lua_regex_cache_max_entries 2;\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]+)\", \"o\")\n            ngx.say(m and m[0])\n\n            m = ngx.re.match(\"howdy, 567\", \"([0-9]+)\", \"oi\")\n            ngx.say(m and m[0])\n\n            m = ngx.re.match(\"hiya, 98\", \"([0-9]+)\", \"ox\")\n            ngx.say(m and m[0])\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n567\n98\n\n\n\n=== TEST 29: disable regex cache completely\n--- http_config\n    lua_regex_cache_max_entries 0;\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]+)\", \"o\")\n            ngx.say(m and m[0])\n\n            m = ngx.re.match(\"howdy, 567\", \"([0-9]+)\", \"oi\")\n            ngx.say(m and m[0])\n\n            m = ngx.re.match(\"hiya, 98\", \"([0-9]+)\", \"ox\")\n            ngx.say(m and m[0])\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n567\n98\n\n\n\n=== TEST 30: named subpatterns w/ extraction\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"(?<first>[a-z]+), [0-9]+\", \"o\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m.first)\n                ngx.say(m.second)\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, 1234\nhello\nhello\nnil\n\n\n\n=== TEST 31: duplicate named subpatterns w/ extraction\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"(?<first>[a-z]+), (?<first>[0-9]+)\", \"Do\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n                ngx.say(table.concat(m.first,\"-\"))\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, 1234\nhello\n1234\nhello-1234\n\n\n\n=== TEST 32: named captures are empty strings\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"1234\", \"(?<first>[a-z]*)([0-9]+)\", \"o\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m.first)\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n\n1234\n\n\n\n=== TEST 33: named captures are false\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, world\", \"(world)|(hello)|(?<named>howdy)\", \"o\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n                ngx.say(m[3])\n                ngx.say(m[\"named\"])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nfalse\nhello\nfalse\nfalse\n"
  },
  {
    "path": "t/039-sub-o.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 6);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: matched but w/o variables\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, world\", \"[a-z]+\", \"howdy\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhowdy, world\n1\n\n\n\n=== TEST 2: not matched\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, world\", \"[A-Z]+\", \"howdy\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world\n0\n\n\n\n=== TEST 3: matched and with variables\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"a b c d\", \"(b) (c)\", \"[$0] [$1] [$2] [$3] [$134]\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\na [b c] [b] [c] [] [] d\n1\n\n\n\n=== TEST 4: matched and with named variables (bad template)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"a b c d\",\n                                         \"(b) (c)\",\n                                         \"[$0] [$1] [$2] [$3] [$hello]\",\n                                         \"o\")\n            if s then\n                ngx.say(s, \": \", n)\n\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: failed to compile the replacement template\n--- error_log\nattempt to use named capturing variable \"hello\" (named captures not supported yet)\n\n\n\n=== TEST 5: matched and with named variables (bracketed)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"a b c d\",\n                                         \"(b) (c)\",\n                                         \"[$0] [$1] [$2] [$3] [${hello}]\",\n                                         \"o\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: failed to compile the replacement template\n--- error_log\nattempt to use named capturing variable \"hello\" (named captures not supported yet)\n\n\n\n=== TEST 6: matched and with bracketed variables\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"b c d\", \"(b) (c)\", \"[$0] [$1] [${2}] [$3] [${134}]\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n[b c] [b] [c] [] [] d\n1\n\n\n\n=== TEST 7: matched and with bracketed variables (unmatched brackets)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"b c d\", \"(b) (c)\", \"[$0] [$1] [${2}] [$3] [${134]\", \"o\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: failed to compile the replacement template\n--- error_log\nthe closing bracket in \"134\" variable is missing\n\n\n\n=== TEST 8: matched and with bracketed variables (unmatched brackets)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"b c d\", \"(b) (c)\", \"[$0] [$1] [${2}] [$3] [${134\", \"o\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: failed to compile the replacement template\n--- error_log\nthe closing bracket in \"134\" variable is missing\n\n\n\n=== TEST 9: matched and with bracketed variables (unmatched brackets)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"b c d\", \"(b) (c)\", \"[$0] [$1] [${2}] [$3] [${\", \"o\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: failed to compile the replacement template\n--- error_log\nlua script: invalid capturing variable name found in \"[$0] [$1] [${2}] [$3] [${\"\n\n\n\n=== TEST 10: trailing $\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"b c d\", \"(b) (c)\", \"[$0] [$1] [${2}] [$3] [$\", \"o\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: failed to compile the replacement template\n--- error_log\nlua script: invalid capturing variable name found in \"[$0] [$1] [${2}] [$3] [$\"\n\n\n\n=== TEST 11: matched but w/o variables and with literal $\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, world\", \"[a-z]+\", \"ho$$wdy\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nho$wdy, world\n1\n\n\n\n=== TEST 12: non-anchored match\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, 1234\", \" [0-9] \", \"x\", \"xo\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, x234\n1\n\n\n\n=== TEST 13: anchored match\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, 1234\", \"[0-9]\", \"x\", \"ao\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, 1234\n0\n\n\n\n=== TEST 14: function replace\n--- config\n    location /re {\n        content_by_lua '\n            local repl = function (m)\n                return \"[\" .. m[0] .. \"] [\" .. m[1] .. \"]\"\n            end\n\n            local s, n = ngx.re.sub(\"hello, 34\", \"([0-9])\", repl, \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, [3] [3]4\n1\n\n\n\n=== TEST 15: function replace (failed)\n--- config\n    location /re {\n        content_by_lua '\n            local repl = function (m)\n                return \"[\" .. m[0] .. \"] [\" .. m[1] .. \"]\"\n            end\n\n            local s, n = ngx.re.sub(\"hello, 34\", \"([A-Z])\", repl, \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, 34\n0\n\n\n\n=== TEST 16: bad repl arg type\n--- SKIP\n--- config\n    location /re {\n        content_by_lua '\n            local rc, s, n = pcall(ngx.re.sub, \"hello, 34\", \"([A-Z])\", true, \"o\")\n            ngx.say(rc)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfalse\nbad argument #3 to '?' (string, number, or function expected, got boolean)\nnil\n\n\n\n=== TEST 17: use number to replace\n--- config\n    location /re {\n        content_by_lua '\n            local rc, s, n = pcall(ngx.re.sub, \"hello, 34\", \"([0-9])\", 72, \"o\")\n            ngx.say(rc)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ntrue\nhello, 724\n1\n\n\n\n=== TEST 18: bad function return value type\n--- SKIP\n--- config\n    location /re {\n        content_by_lua '\n            local f = function (m) end\n            local rc, s, n = pcall(ngx.re.sub, \"hello, 34\", \"([0-9])\", f, \"o\")\n            ngx.say(rc)\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfalse\nbad argument #3 to '?' (string or number expected to be returned by the replace function, got nil)\nnil\n\n\n\n=== TEST 19: matched but w/o variables (set_by_lua)\n--- config\n    location /re {\n        set_by_lua $res '\n            local s, n = ngx.re.sub(\"hello, world\", \"[a-z]+\", \"howdy\", \"o\")\n            return s\n        ';\n        echo $res;\n    }\n--- request\n    GET /re\n--- response_body\nhowdy, world\n\n\n\n=== TEST 20: with regex cache (with text replace)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, 1234\", \"([A-Z]+)\", \"baz\", \"io\")\n            ngx.say(s)\n            ngx.say(n)\n\n            local s, n = ngx.re.sub(\"howdy, 1234\", \"([A-Z]+)\", \"baz\", \"io\")\n            ngx.say(s)\n            ngx.say(n)\n\n\n            s, n = ngx.re.sub(\"1234, okay\", \"([A-Z]+)\", \"blah\", \"io\")\n            ngx.say(s)\n            ngx.say(n)\n\n            s, n = ngx.re.sub(\"hi, 1234\", \"([A-Z]+)\", \"hello\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nbaz, 1234\n1\nbaz, 1234\n1\n1234, blah\n1\nhi, 1234\n0\n\n\n\n=== TEST 21: with regex cache (with func replace)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, 1234\", \"([A-Z]+)\", \"baz\", \"io\")\n            ngx.say(s)\n            ngx.say(n)\n\n            local s, n = ngx.re.sub(\"howdy, 1234\", \"([A-Z]+)\", function () return \"bah\" end, \"io\")\n            ngx.say(s)\n            ngx.say(n)\n\n            s, n = ngx.re.sub(\"1234, okay\", \"([A-Z]+)\", function () return \"blah\" end, \"io\")\n            ngx.say(s)\n            ngx.say(n)\n\n            s, n = ngx.re.sub(\"hi, 1234\", \"([A-Z]+)\", \"hello\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nbaz, 1234\n1\nbah, 1234\n1\n1234, blah\n1\nhi, 1234\n0\n\n\n\n=== TEST 22: exceeding regex cache max entries\n--- http_config\n    lua_regex_cache_max_entries 2;\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, 1234\", \"([0-9]+)\", \"hello\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n\n            s, n = ngx.re.sub(\"howdy, 567\", \"([0-9]+)\", \"hello\", \"oi\")\n            ngx.say(s)\n            ngx.say(n)\n\n            s, n = ngx.re.sub(\"hiya, 98\", \"([0-9]+)\", \"hello\", \"ox\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, hello\n1\nhowdy, hello\n1\nhiya, hello\n1\n\n\n\n=== TEST 23: disable regex cache completely\n--- http_config\n    lua_regex_cache_max_entries 0;\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, 1234\", \"([0-9]+)\", \"hello\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n\n            s, n = ngx.re.sub(\"howdy, 567\", \"([0-9]+)\", \"hello\", \"oi\")\n            ngx.say(s)\n            ngx.say(n)\n\n            s, n = ngx.re.sub(\"hiya, 98\", \"([0-9]+)\", \"hello\", \"ox\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, hello\n1\nhowdy, hello\n1\nhiya, hello\n1\n\n\n\n=== TEST 24: empty replace\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, 1234\", \"([0-9]+)\", \"\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n\n            local s, n = ngx.re.sub(\"hi, 5432\", \"([0-9]+)\", \"\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, \n1\nhi, \n1\n\n\n\n=== TEST 25: matched and with variables w/o using named patterns in sub\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"a b c d\", \"(?<first>b) (?<second>c)\", \"[$0] [$1] [$2] [$3] [$134]\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\na [b c] [b] [c] [] [] d\n1\n\n\n\n=== TEST 26: matched and with variables using named patterns in func\n--- config\n    error_log /tmp/nginx_error debug;\n    location /re {\n        content_by_lua '\n            local repl = function (m)\n                return \"[\" .. m[0] .. \"] [\" .. m[\"first\"] .. \"] [\" .. m[2] .. \"]\"\n            end\n\n            local s, n = ngx.re.sub(\"a b c d\", \"(?<first>b) (?<second>c)\", repl, \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\na [b c] [b] [c] d\n1\n"
  },
  {
    "path": "t/040-gsub-o.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"[hello, world]\", \"[a-z]+\", \"howdy\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n[howdy, howdy]\n2\n\n\n\n=== TEST 2: trimmed\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, world\", \"[a-z]+\", \"howdy\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhowdy, howdy\n2\n\n\n\n=== TEST 3: not matched\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, world\", \"[A-Z]+\", \"howdy\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world\n0\n\n\n\n=== TEST 4: replace by function (trimmed)\n--- config\n    location /re {\n        content_by_lua '\n            local f = function (m)\n                return \"[\" .. m[0] .. \",\" .. m[1] .. \"]\"\n            end\n\n            local s, n = ngx.re.gsub(\"hello, world\", \"([a-z])[a-z]+\", f, \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n[hello,h], [world,w]\n2\n\n\n\n=== TEST 5: replace by function (not trimmed)\n--- config\n    location /re {\n        content_by_lua '\n            local f = function (m)\n                return \"[\" .. m[0] .. \",\" .. m[1] .. \"]\"\n            end\n\n            local s, n = ngx.re.gsub(\"{hello, world}\", \"([a-z])[a-z]+\", f, \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n{[hello,h], [world,w]}\n2\n\n\n\n=== TEST 6: replace by script (trimmed)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, world\", \"([a-z])[a-z]+\", \"[$0,$1]\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n[hello,h], [world,w]\n2\n\n\n\n=== TEST 7: replace by script (not trimmed)\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"{hello, world}\", \"([a-z])[a-z]+\", \"[$0,$1]\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n{[hello,h], [world,w]}\n2\n\n\n\n=== TEST 8: set_by_lua\n--- config\n    location /re {\n        set_by_lua $res '\n            local f = function (m)\n                return \"[\" .. m[0] .. \",\" .. m[1] .. \"]\"\n            end\n\n            local s, n = ngx.re.gsub(\"{hello, world}\", \"([a-z])[a-z]+\", f, \"o\")\n            return s\n        ';\n        echo $res;\n    }\n--- request\n    GET /re\n--- response_body\n{[hello,h], [world,w]}\n\n\n\n=== TEST 9: look-behind assertion\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"{foobarbaz}\", \"(?<=foo)bar|(?<=bar)baz\", \"h$0\", \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n{foohbarhbaz}\n2\n\n\n\n=== TEST 10: named pattern repl w/ callback\n--- config\n    location /re {\n       content_by_lua '\n            local repl = function (m)\n                return \"[\" .. m[0] .. \",\" .. m[\"first\"] .. \"]\"\n            end\n\n            local s, n = ngx.re.gsub(\"hello, world\", \"(?<first>[a-z])[a-z]+\", repl, \"o\")\n            ngx.say(s)\n            ngx.say(n)\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n[hello,h], [world,w]\n2\n"
  },
  {
    "path": "t/041-header-filter.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nlog_level('debug');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 13);\n\n#no_diff();\n#no_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: set response content-type header\n--- config\n    location /read {\n        echo \"Hi\";\n        header_filter_by_lua '\n            ngx.header.content_type = \"text/my-plain\";\n        ';\n\n    }\n--- request\nGET /read\n--- response_headers\nContent-Type: text/my-plain\n--- response_body\nHi\n\n\n\n=== TEST 2: server config\n--- config\n    header_filter_by_lua '\n        ngx.header.content_type = \"text/my-plain\";\n    ';\n\n    location /read {\n        echo \"Hi\";\n\n    }\n--- request\nGET /read\n--- response_headers\nContent-Type: text/my-plain\n--- response_body\nHi\n\n\n\n=== TEST 3: set in http\n--- http_config\n    header_filter_by_lua '\n        ngx.header.content_type = \"text/my-plain\";\n    ';\n--- config\n    location /read {\n        echo \"Hi\";\n    }\n--- request\nGET /read\n--- response_headers\nContent-Type: text/my-plain\n--- response_body\nHi\n\n\n\n=== TEST 4: overriding config\n--- config\n    header_filter_by_lua '\n        ngx.header.content_type = \"text/my-plain\";\n    ';\n    location /read {\n        echo \"Hi\";\n        header_filter_by_lua '\n            ngx.header.content_type = \"text/read-plain\";\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nContent-Type: text/read-plain\n--- response_body\nHi\n\n\n\n=== TEST 5: set response content-type header\n--- config\n    location /read {\n        echo \"Hi\";\n        header_filter_by_lua '\n            ngx.header.content_type = \"text/my-plain\";\n        ';\n\n    }\n--- request\nGET /read\n--- response_headers\nContent-Type: text/my-plain\n--- response_body\nHi\n\n\n\n=== TEST 6: lua code run failed\n--- config\n    location /read {\n        echo \"Hi\";\n        header_filter_by_lua '\n            ngx.header.content_length = \"text/my-plain\";\n        ';\n    }\n--- request\nGET /read\n--- error_code\n--- response_body\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 7: use variable generated by content phrase\n--- config\n   location /read {\n        set $strvar '1';\n        content_by_lua '\n            ngx.var.strvar = \"127.0.0.1:8080\";\n            ngx.say(\"Hi\");\n        ';\n        header_filter_by_lua '\n            ngx.header.uid = ngx.var.strvar;\n        ';\n    }\n--- request\nGET /read\n--- response_headers\nuid: 127.0.0.1:8080\n--- response_body\nHi\n\n\n\n=== TEST 8: use variable generated by content phrase for HEAD\n--- config\n   location /read {\n        set $strvar '1';\n        content_by_lua '\n            ngx.var.strvar = \"127.0.0.1:8080\";\n            ngx.say(\"Hi\");\n        ';\n        header_filter_by_lua '\n            ngx.header.uid = ngx.var.strvar;\n        ';\n    }\n--- request\nHEAD /read\n--- response_headers\nuid: 127.0.0.1:8080\n--- response_body\n\n\n\n=== TEST 9: use variable generated by content phrase for HTTP 1.0\n--- config\n   location /read {\n        set $strvar '1';\n        content_by_lua '\n            ngx.var.strvar = \"127.0.0.1:8080\";\n            ngx.say(\"Hi\");\n        ';\n        header_filter_by_lua '\n            ngx.header.uid = ngx.var.strvar;\n        ';\n\n    }\n--- request\nGET /read HTTP/1.0\n--- response_headers\nuid: 127.0.0.1:8080\n--- response_body\nHi\n\n\n\n=== TEST 10: use capture and header_filter_by\n--- config\n   location /sub {\n        content_by_lua '\n            ngx.say(\"Hi\");\n        ';\n        header_filter_by_lua '\n            ngx.header.uid = \"sub\";\n        ';\n    }\n\n    location /parent {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            if res.status == 200 then\n                ngx.say(res.header.uid)\n            else\n                ngx.say(\"parent\")\n            end\n        ';\n        header_filter_by_lua '\n            ngx.header.uid = \"parent\";\n        ';\n    }\n\n--- request\nGET /parent\n--- response_headers\nuid: parent\n--- response_body\nsub\n\n\n\n=== TEST 11: overriding ctx\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.ctx.foo = 32;\n            ngx.say(ngx.ctx.foo)\n        ';\n        header_filter_by_lua '\n            ngx.ctx.foo = ngx.ctx.foo + 1;\n            ngx.header.uid = ngx.ctx.foo;\n        ';\n    }\n--- request\nGET /lua\n--- response_headers\nuid: 33\n--- response_body\n32\n\n\n\n=== TEST 12: use req\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(\"Hi\");\n        ';\n\n        header_filter_by_lua '\n            local str = \"\";\n            local args, err = ngx.req.get_uri_args()\n            if err then\n                ngx.log(ngx.ERR, \"err: \", err)\n                return ngx.exit(500)\n            end\n            local keys = {}\n            for key, val in pairs(args) do\n                table.insert(keys, key)\n            end\n            table.sort(keys)\n            for i, key in ipairs(keys) do\n                local val = args[key]\n                if type(val) == \"table\" then\n                    str = str .. table.concat(val, \", \")\n                else\n                    str = str .. \":\" .. val\n                end\n            end\n\n            ngx.header.uid = str;\n        ';\n    }\n--- request\nGET /lua?a=1&b=2\n--- response_headers\nuid: :1:2\n--- response_body\nHi\n\n\n\n=== TEST 13: use ngx md5 function\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(\"Hi\");\n        ';\n        header_filter_by_lua '\n            ngx.header.uid = ngx.md5(\"Hi\");\n        ';\n    }\n--- request\nGET /lua\n--- response_headers\nuid: c1a5298f939e87e8f962a5edfc206918\n--- response_body\nHi\n\n\n\n=== TEST 14: set response content-type header (by file)\n--- config\n    location /read {\n        echo \"Hi\";\n        header_filter_by_lua_file 'html/foo.lua';\n    }\n--- request\nGET /read\n--- user_files\n>>> foo.lua\nngx.header.content_type = \"text/my-plain\";\n--- response_headers\nContent-Type: text/my-plain\n--- response_body\nHi\n\n\n\n=== TEST 15: by_lua_file server config\n--- config\n    header_filter_by_lua_file 'html/foo.lua';\n\n    location /read {\n        echo \"Hi\";\n    }\n--- request\nGET /read\n--- user_files\n>>> foo.lua\nngx.header.content_type = \"text/my-plain\";\n--- response_headers\nContent-Type: text/my-plain\n--- response_body\nHi\n\n\n\n=== TEST 16: by_lua_file set in http\n--- http_config\n    header_filter_by_lua_file 'html/foo.lua';\n--- config\n    location /read {\n        echo \"Hi\";\n    }\n--- request\nGET /read\n--- user_files\n>>> foo.lua\nngx.header.content_type = \"text/my-plain\";\n--- response_headers\nContent-Type: text/my-plain\n--- response_body\nHi\n\n\n\n=== TEST 17: by_lua_file overriding config\n--- config\n    header_filter_by_lua 'html/foo.lua';\n    location /read {\n        echo \"Hi\";\n        header_filter_by_lua_file 'html/bar.lua';\n    }\n--- request\nGET /read\n--- user_files\n>>> foo.lua\nngx.header.content_type = \"text/my-plain\";\n>>> bar.lua\nngx.header.content_type = \"text/read-plain\";\n--- response_headers\nContent-Type: text/read-plain\n--- response_body\nHi\n\n\n\n=== TEST 18: ngx.ctx available in header_filter_by_lua (already defined)\n--- config\n    location /lua {\n        content_by_lua 'ngx.ctx.counter = 3 ngx.say(ngx.ctx.counter)';\n        header_filter_by_lua 'ngx.log(ngx.ERR, \"ngx.ctx.counter: \", ngx.ctx.counter)';\n    }\n--- request\nGET /lua\n--- response_body\n3\n--- error_log\nngx.ctx.counter: 3\nlua release ngx.ctx\n\n\n\n=== TEST 19: ngx.ctx available in header_filter_by_lua (not defined yet)\n--- config\n    location /lua {\n        echo hello;\n        header_filter_by_lua '\n            ngx.log(ngx.ERR, \"ngx.ctx.counter: \", ngx.ctx.counter)\n            ngx.ctx.counter = \"hello world\"\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nhello\n--- error_log\nngx.ctx.counter: nil\nlua release ngx.ctx\n\n\n\n=== TEST 20: globals are shared by all requests\n--- config\n    location /lua {\n        set $foo '';\n        content_by_lua '\n            ngx.send_headers()\n            ngx.say(ngx.var.foo)\n        ';\n        header_filter_by_lua '\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.INFO, \"old foo: \", foo)\n                foo = foo + 1\n            end\n            ngx.var.foo = foo\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like\n^[12]$\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/old foo: \\d+/\n--- grep_error_log_out eval\n[\"\", \"old foo: 1\\n\"]\n\n\n\n=== TEST 21: lua error (string)\n--- no_http2\n--- config\n    location /lua {\n        set $foo '';\n        content_by_lua '\n            ngx.send_headers()\n            ngx.say(ngx.var.foo)\n        ';\n        header_filter_by_lua '\n            error(\"Something bad\")\n        ';\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nfailed to run header_filter_by_lua*: header_filter_by_lua(nginx.conf:47):2: Something bad\n--- no_error_log\n[alert]\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 22: lua error (nil)\n--- no_http2\n--- config\n    location /lua {\n        set $foo '';\n        content_by_lua '\n            ngx.send_headers()\n            ngx.say(ngx.var.foo)\n        ';\n        header_filter_by_lua '\n            error(nil)\n        ';\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nfailed to run header_filter_by_lua*: unknown reason\n--- no_error_log\n[alert]\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 23: no ngx.print\n--- config\n    location /lua {\n        header_filter_by_lua \"ngx.print(32) return 1\";\n        echo ok;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of header_filter_by_lua*\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 24: no ngx.say\n--- config\n    location /lua {\n        header_filter_by_lua \"ngx.say(32) return 1\";\n        echo ok;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of header_filter_by_lua*\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 25: no ngx.flush\n--- config\n    location /lua {\n        header_filter_by_lua \"ngx.flush()\";\n        echo ok;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of header_filter_by_lua*\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 26: no ngx.eof\n--- config\n    location /lua {\n        header_filter_by_lua \"ngx.eof()\";\n        echo ok;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of header_filter_by_lua*\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 27: no ngx.send_headers\n--- config\n    location /lua {\n        header_filter_by_lua \"ngx.send_headers()\";\n        echo ok;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of header_filter_by_lua*\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 28: no ngx.location.capture\n--- config\n    location /lua {\n        header_filter_by_lua 'ngx.location.capture(\"/sub\")';\n        echo ok;\n    }\n\n    location /sub {\n        echo sub;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of header_filter_by_lua*\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 29: no ngx.location.capture_multi\n--- config\n    location /lua {\n        header_filter_by_lua 'ngx.location.capture_multi{{\"/sub\"}}';\n        echo ok;\n    }\n\n    location /sub {\n        echo sub;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of header_filter_by_lua*\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 30: no ngx.redirect\n--- config\n    location /lua {\n        header_filter_by_lua 'ngx.redirect(\"/blah\")';\n        echo ok;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of header_filter_by_lua*\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 31: no ngx.exec\n--- config\n    location /lua {\n        header_filter_by_lua 'ngx.exec(\"/blah\")';\n        echo ok;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of header_filter_by_lua*\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 32: no ngx.req.set_uri(uri, true)\n--- config\n    location /lua {\n        header_filter_by_lua 'ngx.req.set_uri(\"/blah\", true)';\n        echo ok;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of header_filter_by_lua*\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 33: ngx.req.set_uri(uri) exists\n--- config\n    location /lua {\n        header_filter_by_lua 'ngx.req.set_uri(\"/blah\") return 1';\n        content_by_lua '\n            ngx.send_headers()\n            ngx.say(\"uri: \", ngx.var.uri)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nuri: /blah\n--- no_error_log\n[error]\n\n\n\n=== TEST 34: no ngx.req.read_body()\n--- config\n    location /lua {\n        header_filter_by_lua 'ngx.req.read_body()';\n        echo ok;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log eval\nqr/API disabled in the context of header_filter_by_lua\\*|http3 requests are not supported without content-length header/ms\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 35: no ngx.req.socket()\n--- config\n    location /lua {\n        header_filter_by_lua 'return ngx.req.socket()';\n        echo ok;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log eval\nmy $err_log;\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $err_log = \"http v3 not supported yet\";\n} else {\n    $err_log = \"API disabled in the context of header_filter_by_lua*\";\n}\n\n$err_log;\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 36: no ngx.socket.tcp()\n--- config\n    location /lua {\n        header_filter_by_lua 'return ngx.socket.tcp()';\n        echo ok;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of header_filter_by_lua*\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 37: no ngx.socket.connect()\n--- config\n    location /lua {\n        header_filter_by_lua 'return ngx.socket.connect(\"127.0.0.1\", 80)';\n        echo ok;\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of header_filter_by_lua*\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 38: clear content-length\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header.content_length = 12\n            ngx.say(\"hello world\")\n        ';\n        header_filter_by_lua 'ngx.header.content_length = nil';\n    }\n--- request\nGET /lua\n--- response_headers\n!content-length\n--- response_body\nhello world\n\n\n\n=== TEST 39: backtrace\n--- config\n    location /t {\n        header_filter_by_lua '\n            local bar\n            local function foo()\n                bar()\n            end\n\n            function bar()\n                error(\"something bad happened\")\n            end\n\n            foo()\n        ';\n        echo ok;\n    }\n--- request\n    GET /t\n--- ignore_response\n--- error_log\nsomething bad happened\nstack traceback:\nin function 'error'\nin function 'bar'\nin function 'foo'\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 40: Lua file does not exist\n--- config\n    location /lua {\n        echo ok;\n        header_filter_by_lua_file html/test2.lua;\n    }\n--- user_files\n>>> test.lua\nv = ngx.var[\"request_uri\"]\nngx.print(\"request_uri: \", v, \"\\n\")\n--- request\nGET /lua?a=1&b=2\n--- ignore_response\n--- error_log eval\nqr/failed to load external Lua file \".*?test2\\.lua\": cannot open .*? No such file or directory/\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 41: filter finalize\n--- config\n    error_page 582 = /bar;\n    location = /t {\n        echo ok;\n        header_filter_by_lua '\n            return ngx.exit(582)\n        ';\n    }\n\n    location = /bar {\n        echo hi;\n        header_filter_by_lua '\n            return ngx.exit(302)\n        ';\n    }\n--- request\nGET /t\n--- response_body_like: 302 Found\n--- error_code: 302\n--- no_error_log\n[error]\n--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n\n\n\n=== TEST 42: syntax error in header_filter_by_lua_block\n--- config\n    location /lua {\n\n        header_filter_by_lua_block {\n            'for end';\n        }\n        content_by_lua_block {\n            ngx.say(\"Hello world\")\n        }\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nfailed to load inlined Lua code: header_filter_by_lua(nginx.conf:41):2: unexpected symbol near ''for end''\n--- no_error_log\nno_such_error\n--- curl_error eval\nqr/curl: \\(56\\) Failure when receiving data from the peer|curl: \\(92\\) HTTP\\/2 stream 1 was not closed cleanly: INTERNAL_ERROR \\(err 2\\)/\n\n\n\n=== TEST 43: syntax error in second content_by_lua_block\n--- config\n    location /foo {\n        header_filter_by_lua_block {\n            'for end';\n        }\n        content_by_lua_block {\n            ngx.say(\"Hello world\")\n        }\n    }\n\n    location /lua {\n        header_filter_by_lua_block {\n            'for end';\n        }\n        content_by_lua_block {\n            ngx.say(\"Hello world\")\n        }\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nfailed to load inlined Lua code: header_filter_by_lua(nginx.conf:49):2: unexpected symbol near ''for end''\n--- no_error_log\nno_such_error\n--- curl_error eval\nqr/curl: \\(56\\) Failure when receiving data from the peer|curl: \\(92\\) HTTP\\/2 stream 1 was not closed cleanly: INTERNAL_ERROR \\(err 2\\)/\n\n\n\n=== TEST 44: syntax error in /tmp/12345678901234567890123456789012345.conf\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"Hello world\")\n        }\n\n        include /tmp/12345678901234567890123456789012345.conf;\n    }\n--- user_files\n>>> /tmp/12345678901234567890123456789012345.conf\n    header_filter_by_lua_block {\n        'for end';\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nfailed to load inlined Lua code: header_filter_by_lua(...901234567890123456789012345.conf:1):2: unexpected symbol near ''for end''\n--- no_error_log\n[alert]\n--- curl_error eval\nqr/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\\)/\n"
  },
  {
    "path": "t/042-crc32.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: short sanity\n--- config\n    location = /test {\n        content_by_lua '\n            ngx.say(ngx.crc32_short(\"hello, world\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\n4289425978\n\n\n\n=== TEST 2: long sanity\n--- config\n    location = /test {\n        content_by_lua '\n            ngx.say(ngx.crc32_long(\"hello, world\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\n4289425978\n\n\n\n=== TEST 3: long sanity (empty)\n--- config\n    location = /test {\n        content_by_lua '\n            ngx.say(ngx.crc32_long(\"\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\n0\n"
  },
  {
    "path": "t/043-shdict.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\n#repeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 17);\n\n#no_diff();\nno_long_string();\n#master_on();\n#workers(2);\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: string key, int value\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            dogs:set(\"bah\", 10502)\n            local val = dogs:get(\"foo\")\n            ngx.say(val, \" \", type(val))\n            val = dogs:get(\"bah\")\n            ngx.say(val, \" \", type(val))\n        ';\n    }\n--- request\nGET /test\n--- response_body\n32 number\n10502 number\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: string key, floating-point value\n--- http_config\n    lua_shared_dict cats 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local cats = ngx.shared.cats\n            cats:set(\"foo\", 3.14159)\n            cats:set(\"baz\", 1.28)\n            cats:set(\"baz\", 3.96)\n            local val = cats:get(\"foo\")\n            ngx.say(val, \" \", type(val))\n            val = cats:get(\"baz\")\n            ngx.say(val, \" \", type(val))\n        ';\n    }\n--- request\nGET /test\n--- response_body\n3.14159 number\n3.96 number\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: string key, boolean value\n--- http_config\n    lua_shared_dict cats 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local cats = ngx.shared.cats\n            cats:set(\"foo\", true)\n            cats:set(\"bar\", false)\n            local val = cats:get(\"foo\")\n            ngx.say(val, \" \", type(val))\n            val = cats:get(\"bar\")\n            ngx.say(val, \" \", type(val))\n        ';\n    }\n--- request\nGET /test\n--- response_body\ntrue boolean\nfalse boolean\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: number keys, string values\n--- http_config\n    lua_shared_dict cats 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local cats = ngx.shared.cats\n            ngx.say(cats:set(1234, \"cat\"))\n            ngx.say(cats:set(\"1234\", \"dog\"))\n            ngx.say(cats:set(256, \"bird\"))\n            ngx.say(cats:get(1234))\n            ngx.say(cats:get(\"1234\"))\n            local val = cats:get(\"256\")\n            ngx.say(val, \" \", type(val))\n        ';\n    }\n--- request\nGET /test\n--- response_body\ntruenilfalse\ntruenilfalse\ntruenilfalse\ndog\ndog\nbird string\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: different-size values set to the same key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", \"hello\")\n            ngx.say(dogs:get(\"foo\"))\n            dogs:set(\"foo\", \"hello, world\")\n            ngx.say(dogs:get(\"foo\"))\n            dogs:set(\"foo\", \"hello\")\n            ngx.say(dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nhello\nhello, world\nhello\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: expired entries (can be auto-removed by get)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32, 0.01)\n            ngx.location.capture(\"/sleep/0.011\")\n            ngx.say(dogs:get(\"foo\"))\n        ';\n    }\n    location ~ '^/sleep/(.+)' {\n        echo_sleep $1;\n    }\n--- request\nGET /test\n--- response_body\nnil\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: expired entries (can NOT be auto-removed by get)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"bar\", 56, 0.001)\n            dogs:set(\"baz\", 78, 0.001)\n            dogs:set(\"foo\", 32, 0.01)\n            ngx.location.capture(\"/sleep/0.012\")\n            ngx.say(dogs:get(\"foo\"))\n        ';\n    }\n    location ~ '^/sleep/(.+)' {\n        echo_sleep $1;\n    }\n--- request\nGET /test\n--- response_body\nnil\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: not yet expired entries\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32, 0.5)\n            ngx.location.capture(\"/sleep/0.01\")\n            ngx.say(dogs:get(\"foo\"))\n        ';\n    }\n    location ~ '^/sleep/(.+)' {\n        echo_sleep $1;\n    }\n--- request\nGET /test\n--- response_body\n32\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: forcibly override other valid entries\n--- http_config\n    lua_shared_dict dogs 100k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local i = 0\n            while i < 1000 do\n                i = i + 1\n                local val = string.rep(\" hello\", 10) .. i\n                local res, err, forcible = dogs:set(\"key_\" .. i, val)\n                if not res or forcible then\n                    ngx.say(res, \" \", err, \" \", forcible)\n                    break\n                end\n            end\n            ngx.say(\"abort at \", i)\n            ngx.say(\"cur value: \", dogs:get(\"key_\" .. i))\n            if i > 1 then\n                ngx.say(\"1st value: \", dogs:get(\"key_1\"))\n            end\n            if i > 2 then\n                ngx.say(\"2nd value: \", dogs:get(\"key_2\"))\n            end\n        ';\n    }\n--- pipelined_requests eval\n[\"GET /test\", \"GET /test\"]\n--- response_body eval\nmy $a = \"true nil true\\nabort at (353|705)\\ncur value: \" . (\" hello\" x 10) . \"\\\\1\\n1st value: nil\\n2nd value: \" . (\" hello\" x 10) . \"2\\n\";\n[qr/$a/,\n\"true nil true\\nabort at 1\\ncur value: \" . (\" hello\" x 10) . \"1\\n\"\n]\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: forcibly override other valid entries and test LRU\n--- http_config\n    lua_shared_dict dogs 100k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local i = 0\n            while i < 1000 do\n                i = i + 1\n                local val = string.rep(\" hello\", 10) .. i\n                if i == 10 then\n                    dogs:get(\"key_1\")\n                end\n                local res, err, forcible = dogs:set(\"key_\" .. i, val)\n                if not res or forcible then\n                    ngx.say(res, \" \", err, \" \", forcible)\n                    break\n                end\n            end\n            ngx.say(\"abort at \", i)\n            ngx.say(\"cur value: \", dogs:get(\"key_\" .. i))\n            if i > 1 then\n                ngx.say(\"1st value: \", dogs:get(\"key_1\"))\n            end\n            if i > 2 then\n                ngx.say(\"2nd value: \", dogs:get(\"key_2\"))\n            end\n        ';\n    }\n--- pipelined_requests eval\n[\"GET /test\", \"GET /test\"]\n--- response_body eval\nmy $a = \"true nil true\\nabort at (353|705)\\ncur value: \" . (\" hello\" x 10) . \"\\\\1\\n1st value: \" . (\" hello\" x 10) . \"1\\n2nd value: nil\\n\";\n[qr/$a/,\n\"true nil true\\nabort at 2\\ncur value: \" . (\" hello\" x 10) . \"2\\n1st value: \" . (\" hello\" x 10) . \"1\\n\"\n]\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: dogs and cats dicts\n--- http_config\n    lua_shared_dict dogs 1m;\n    lua_shared_dict cats 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local cats = ngx.shared.cats\n            dogs:set(\"foo\", 32)\n            cats:set(\"foo\", \"hello, world\")\n            ngx.say(dogs:get(\"foo\"))\n            ngx.say(cats:get(\"foo\"))\n            dogs:set(\"foo\", 56)\n            ngx.say(dogs:get(\"foo\"))\n            ngx.say(cats:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\n32\nhello, world\n56\nhello, world\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: get non-existent keys\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            ngx.say(dogs:get(\"foo\"))\n            ngx.say(dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnil\nnil\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: not feed the object into the call\n--- SKIP\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local rc, err = pcall(dogs.set, \"foo\", 3, 0.01)\n            ngx.say(rc, \" \", err)\n            rc, err = pcall(dogs.set, \"foo\", 3)\n            ngx.say(rc, \" \", err)\n            rc, err = pcall(dogs.get, \"foo\")\n            ngx.say(rc, \" \", err)\n        ';\n    }\n--- request\nGET /test\n--- response_body\nfalse bad argument #1 to '?' (userdata expected, got string)\nfalse expecting 3, 4 or 5 arguments, but only seen 2\nfalse expecting exactly two arguments, but only seen 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: too big value\n--- http_config\n    lua_shared_dict dogs 50k;\n--- config\n    location = /test {\n        content_by_lua '\n            collectgarbage(\"collect\")\n            local dogs = ngx.shared.dogs\n            local res, err, forcible = dogs:set(\"foo\", string.rep(\"helloworld\", 10000))\n            ngx.say(res, \" \", err, \" \", forcible)\n        ';\n    }\n--- request\nGET /test\n--- response_body\nfalse no memory false\n--- log_level: info\n--- no_error_log\n[error]\n[crit]\nngx_slab_alloc() failed: no memory in lua_shared_dict zone\n\n\n\n=== TEST 15: set too large key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local key = string.rep(\"a\", 65535)\n            local rc, err = dogs:set(key, \"hello\")\n            ngx.say(rc, \" \", err)\n            ngx.say(dogs:get(key))\n\n            key = string.rep(\"a\", 65536)\n            local ok, err = dogs:set(key, \"world\")\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n\n        ';\n    }\n--- request\nGET /test\n--- response_body\ntrue nil\nhello\nnot ok: key too long\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: bad value type\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:set(\"foo\", dogs)\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot ok: bad value type\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: delete after setting values\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            ngx.say(dogs:get(\"foo\"))\n            dogs:delete(\"foo\")\n            ngx.say(dogs:get(\"foo\"))\n            dogs:set(\"foo\", \"hello, world\")\n            ngx.say(dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\n32\nnil\nhello, world\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: delete at first\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:delete(\"foo\")\n            ngx.say(dogs:get(\"foo\"))\n            dogs:set(\"foo\", \"hello, world\")\n            ngx.say(dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnil\nhello, world\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: set nil after setting values\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            ngx.say(dogs:get(\"foo\"))\n            dogs:set(\"foo\", nil)\n            ngx.say(dogs:get(\"foo\"))\n            dogs:set(\"foo\", \"hello, world\")\n            ngx.say(dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\n32\nnil\nhello, world\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: set nil at first\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", nil)\n            ngx.say(dogs:get(\"foo\"))\n            dogs:set(\"foo\", \"hello, world\")\n            ngx.say(dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnil\nhello, world\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: fail to allocate memory\n--- http_config\n    lua_shared_dict dogs 100k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local i = 0\n            while i < 1000 do\n                i = i + 1\n                local val = string.rep(\"hello\", i )\n                local res, err, forcible = dogs:set(\"key_\" .. i, val)\n                if not res or forcible then\n                    ngx.say(res, \" \", err, \" \", forcible)\n                    break\n                end\n            end\n            ngx.say(\"abort at \", i)\n        ';\n    }\n--- request\nGET /test\n--- response_body_like\n^true nil true\\nabort at (?:141|140)$\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: string key, int value (write_by_lua)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        rewrite_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            dogs:set(\"bah\", 10502)\n            local val = dogs:get(\"foo\")\n            ngx.say(val, \" \", type(val))\n            val = dogs:get(\"bah\")\n            ngx.say(val, \" \", type(val))\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /test\n--- response_body\n32 number\n10502 number\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: string key, int value (access_by_lua)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        access_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            dogs:set(\"bah\", 10502)\n            local val = dogs:get(\"foo\")\n            ngx.say(val, \" \", type(val))\n            val = dogs:get(\"bah\")\n            ngx.say(val, \" \", type(val))\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /test\n--- response_body\n32 number\n10502 number\n--- no_error_log\n[error]\n\n\n\n=== TEST 24: string key, int value (set_by_lua)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        set_by_lua $res '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            return dogs:get(\"foo\")\n        ';\n        echo $res;\n    }\n--- request\nGET /test\n--- response_body\n32\n--- no_error_log\n[error]\n\n\n\n=== TEST 25: string key, int value (header_by_lua)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        echo hello;\n        header_filter_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            ngx.header[\"X-Foo\"] = dogs:get(\"foo\")\n        ';\n    }\n--- request\nGET /test\n--- response_headers\nX-Foo: 32\n--- response_body\nhello\n--- no_error_log\n[error]\n\n\n\n=== TEST 26: too big value (forcible)\n--- http_config\n    lua_shared_dict dogs 50k;\n--- config\n    location = /test {\n        content_by_lua '\n            collectgarbage(\"collect\")\n            local dogs = ngx.shared.dogs\n            dogs:set(\"bah\", \"hello\")\n            local res, err, forcible = dogs:set(\"foo\", string.rep(\"helloworld\", 10000))\n            ngx.say(res, \" \", err, \" \", forcible)\n        ';\n    }\n--- request\nGET /test\n--- response_body\nfalse no memory true\n--- log_level: info\n--- no_error_log\n[error]\n[crit]\nngx_slab_alloc() failed: no memory in lua_shared_dict zone\n\n\n\n=== TEST 27: add key (key exists)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            local res, err, forcible = dogs:add(\"foo\", 10502)\n            ngx.say(\"add: \", res, \" \", err, \" \", forcible)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nadd: false exists false\nfoo = 32\n--- no_error_log\n[error]\n\n\n\n=== TEST 28: add key (key not exists)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"bah\", 32)\n            local res, err, forcible = dogs:add(\"foo\", 10502)\n            ngx.say(\"add: \", res, \" \", err, \" \", forcible)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nadd: true nil false\nfoo = 10502\n--- no_error_log\n[error]\n\n\n\n=== TEST 29: add key (key expired)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"bar\", 32, 0.001)\n            dogs:set(\"baz\", 32, 0.001)\n            dogs:set(\"foo\", 32, 0.001)\n            ngx.location.capture(\"/sleep/0.003\")\n            local res, err, forcible = dogs:add(\"foo\", 10502)\n            ngx.say(\"add: \", res, \" \", err, \" \", forcible)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n    location ~ ^/sleep/(.+) {\n        echo_sleep $1;\n    }\n--- request\nGET /test\n--- response_body\nadd: true nil false\nfoo = 10502\n--- no_error_log\n[error]\n\n\n\n=== TEST 30: add key (key expired and value size unmatched)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"bar\", 32, 0.001)\n            dogs:set(\"baz\", 32, 0.001)\n            dogs:set(\"foo\", \"hi\", 0.001)\n            ngx.location.capture(\"/sleep/0.003\")\n            local res, err, forcible = dogs:add(\"foo\", \"hello\")\n            ngx.say(\"add: \", res, \" \", err, \" \", forcible)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n    location ~ ^/sleep/(.+) {\n        echo_sleep $1;\n    }\n--- request\nGET /test\n--- response_body\nadd: true nil false\nfoo = hello\n--- no_error_log\n[error]\n\n\n\n=== TEST 31: replace key (key exists)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            local res, err, forcible = dogs:replace(\"foo\", 10502)\n            ngx.say(\"replace: \", res, \" \", err, \" \", forcible)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n\n            local res, err, forcible = dogs:replace(\"foo\", \"hello\")\n            ngx.say(\"replace: \", res, \" \", err, \" \", forcible)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n\n        ';\n    }\n--- request\nGET /test\n--- response_body\nreplace: true nil false\nfoo = 10502\nreplace: true nil false\nfoo = hello\n--- no_error_log\n[error]\n\n\n\n=== TEST 32: replace key (key not exists)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"bah\", 32)\n            local res, err, forcible = dogs:replace(\"foo\", 10502)\n            ngx.say(\"replace: \", res, \" \", err, \" \", forcible)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nreplace: false not found false\nfoo = nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 33: replace key (key expired)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"bar\", 3, 0.001)\n            dogs:set(\"baz\", 2, 0.001)\n            dogs:set(\"foo\", 32, 0.001)\n            ngx.location.capture(\"/sleep/0.002\")\n            local res, err, forcible = dogs:replace(\"foo\", 10502)\n            ngx.say(\"replace: \", res, \" \", err, \" \", forcible)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n    location ~ ^/sleep/(.+) {\n        echo_sleep $1;\n    }\n--- request\nGET /test\n--- response_body\nreplace: false not found false\nfoo = nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 34: replace key (key expired and value size unmatched)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"bar\", 32, 0.001)\n            dogs:set(\"baz\", 32, 0.001)\n            dogs:set(\"foo\", \"hi\", 0.001)\n            ngx.location.capture(\"/sleep/0.002\")\n            local rc, err, forcible = dogs:replace(\"foo\", \"hello\")\n            ngx.say(\"replace: \", rc, \" \", err, \" \", forcible)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n    location ~ ^/sleep/(.+) {\n        echo_sleep $1;\n    }\n--- request\nGET /test\n--- response_body\nreplace: false not found false\nfoo = nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 35: incr key (key exists)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            local res, err = dogs:incr(\"foo\", 10502)\n            ngx.say(\"incr: \", res, \" \", err)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nincr: 10534 nil\nfoo = 10534\n--- no_error_log\n[error]\n\n\n\n=== TEST 36: incr key (key not exists)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"bah\", 32)\n            local res, err = dogs:incr(\"foo\", 2)\n            ngx.say(\"incr: \", res, \" \", err)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nincr: nil not found\nfoo = nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 37: incr key (key expired)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"bar\", 3, 0.001)\n            dogs:set(\"baz\", 2, 0.001)\n            dogs:set(\"foo\", 32, 0.001)\n            ngx.location.capture(\"/sleep/0.002\")\n            local res, err = dogs:incr(\"foo\", 10502)\n            ngx.say(\"incr: \", res, \" \", err)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n    location ~ ^/sleep/(.+) {\n        echo_sleep $1;\n    }\n--- request\nGET /test\n--- response_body\nincr: nil not found\nfoo = nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 38: incr key (incr by 0)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            local res, err = dogs:incr(\"foo\", 0)\n            ngx.say(\"incr: \", res, \" \", err)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nincr: 32 nil\nfoo = 32\n--- no_error_log\n[error]\n\n\n\n=== TEST 39: incr key (incr by floating point number)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            local res, err = dogs:incr(\"foo\", 0.14)\n            ngx.say(\"incr: \", res, \" \", err)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nincr: 32.14 nil\nfoo = 32.14\n--- no_error_log\n[error]\n\n\n\n=== TEST 40: incr key (incr by negative numbers)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            local res, err = dogs:incr(\"foo\", -0.14)\n            ngx.say(\"incr: \", res, \" \", err)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nincr: 31.86 nil\nfoo = 31.86\n--- no_error_log\n[error]\n\n\n\n=== TEST 41: incr key (original value is not number)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", true)\n            local res, err = dogs:incr(\"foo\", -0.14)\n            ngx.say(\"incr: \", res, \" \", err)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nincr: nil not a number\nfoo = true\n--- no_error_log\n[error]\n\n\n\n=== TEST 42: get and set with flags\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32, 0, 199)\n            dogs:set(\"bah\", 10502, 202)\n            local val, flags = dogs:get(\"foo\")\n            ngx.say(val, \" \", type(val))\n            ngx.say(flags, \" \", type(flags))\n            val, flags = dogs:get(\"bah\")\n            ngx.say(val, \" \", type(val))\n            ngx.say(flags, \" \", type(flags))\n        ';\n    }\n--- request\nGET /test\n--- response_body\n32 number\n199 number\n10502 number\nnil nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 43: expired entries (can be auto-removed by get), with flags set\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32, 0.01, 255)\n            ngx.location.capture(\"/sleep/0.011\")\n            local res, flags = dogs:get(\"foo\")\n            ngx.say(\"res = \", res, \", flags = \", flags)\n        ';\n    }\n    location ~ '^/sleep/(.+)' {\n        echo_sleep $1;\n    }\n--- request\nGET /test\n--- response_body\nres = nil, flags = nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 44: flush_all\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            dogs:set(\"bah\", 10502)\n\n            local val = dogs:get(\"foo\")\n            ngx.say(val, \" \", type(val))\n            val = dogs:get(\"bah\")\n            ngx.say(val, \" \", type(val))\n\n            dogs:flush_all()\n\n            val = dogs:get(\"foo\")\n            ngx.say(val, \" \", type(val))\n            val = dogs:get(\"bah\")\n            ngx.say(val, \" \", type(val))\n        ';\n    }\n--- request\nGET /t\n--- response_body\n32 number\n10502 number\nnil nil\nnil nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 45: flush_expires\n--- quic_max_idle_timeout: 1.6\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", \"x\", 1)\n            dogs:set(\"bah\", \"y\", 0)\n            dogs:set(\"bar\", \"z\", 100)\n\n            ngx.sleep(1.5)\n\n            local num = dogs:flush_expired()\n            ngx.say(num)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n1\n--- no_error_log\n[error]\n\n\n\n=== TEST 46: flush_expires with number\n--- quic_max_idle_timeout: 1.6\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            for i=1,100 do\n                dogs:set(tostring(i), \"x\", 1)\n            end\n\n            dogs:set(\"bah\", \"y\", 0)\n            dogs:set(\"bar\", \"z\", 100)\n\n            ngx.sleep(1.5)\n\n            local num = dogs:flush_expired(42)\n            ngx.say(num)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n42\n--- no_error_log\n[error]\n\n\n\n=== TEST 47: flush_expires an empty dict\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            local num = dogs:flush_expired()\n            ngx.say(num)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n0\n--- no_error_log\n[error]\n\n\n\n=== TEST 48: flush_expires a dict without expired items\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            dogs:set(\"bah\", \"y\", 0)\n            dogs:set(\"bar\", \"z\", 100)\n\n            local num = dogs:flush_expired()\n            ngx.say(num)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n0\n--- no_error_log\n[error]\n\n\n\n=== TEST 49: list all keys in a shdict\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            dogs:set(\"bah\", \"y\", 0)\n            dogs:set(\"bar\", \"z\", 0)\n            local keys = dogs:get_keys()\n            ngx.say(#keys)\n            table.sort(keys)\n            for _,k in ipairs(keys) do\n                ngx.say(k)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\n2\nbah\nbar\n--- no_error_log\n[error]\n\n\n\n=== TEST 50: list keys in a shdict with limit\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            dogs:set(\"bah\", \"y\", 0)\n            dogs:set(\"bar\", \"z\", 0)\n            local keys = dogs:get_keys(1)\n            ngx.say(#keys)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n1\n--- no_error_log\n[error]\n\n\n\n=== TEST 51: list all keys in a shdict with expires\n--- quic_max_idle_timeout: 1.6\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", \"x\", 1)\n            dogs:set(\"bah\", \"y\", 0)\n            dogs:set(\"bar\", \"z\", 100)\n\n            ngx.sleep(1.5)\n\n            local keys = dogs:get_keys()\n            ngx.say(#keys)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n2\n--- no_error_log\n[error]\n\n\n\n=== TEST 52: list keys in a shdict with limit larger than number of keys\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            dogs:set(\"bah\", \"y\", 0)\n            dogs:set(\"bar\", \"z\", 0)\n            local keys = dogs:get_keys(3)\n            ngx.say(#keys)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n2\n--- no_error_log\n[error]\n\n\n\n=== TEST 53: list keys in an empty shdict\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local keys = dogs:get_keys()\n            ngx.say(#keys)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n0\n--- no_error_log\n[error]\n\n\n\n=== TEST 54: list keys in an empty shdict with a limit\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local keys = dogs:get_keys(4)\n            ngx.say(#keys)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n0\n--- no_error_log\n[error]\n\n\n\n=== TEST 55: list all keys in a shdict with all keys expired\n--- quic_max_idle_timeout: 1.6\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", \"x\", 1)\n            dogs:set(\"bah\", \"y\", 1)\n            dogs:set(\"bar\", \"z\", 1)\n\n            ngx.sleep(1.5)\n\n            local keys = dogs:get_keys()\n            ngx.say(#keys)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n0\n--- no_error_log\n[error]\n\n\n\n=== TEST 56: list all keys in a shdict with more than 1024 keys with no limit set\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            for i=1,2048 do\n                dogs:set(tostring(i), i)\n            end\n            local keys = dogs:get_keys()\n            ngx.say(#keys)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n1024\n--- no_error_log\n[error]\n\n\n\n=== TEST 57: list all keys in a shdict with more than 1024 keys with 0 limit set\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /t {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            for i=1,2048 do\n                dogs:set(tostring(i), i)\n            end\n            local keys = dogs:get_keys(0)\n            ngx.say(#keys)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n2048\n--- no_error_log\n[error]\n\n\n\n=== TEST 58: safe_set\n--- http_config\n    lua_shared_dict dogs 100k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local i = 0\n            while i < 1000 do\n                i = i + 1\n                local val = string.rep(\" hello\", 10) .. i\n                local res, err = dogs:safe_set(\"key_\" .. i, val)\n                if not res then\n                    ngx.say(res, \" \", err)\n                    break\n                end\n            end\n            ngx.say(\"abort at \", i)\n            ngx.say(\"cur value: \", dogs:get(\"key_\" .. i))\n            if i > 1 then\n                ngx.say(\"1st value: \", dogs:get(\"key_1\"))\n            end\n            if i > 2 then\n                ngx.say(\"2nd value: \", dogs:get(\"key_2\"))\n            end\n        ';\n    }\n--- pipelined_requests eval\n[\"GET /test\", \"GET /test\"]\n--- response_body eval\nmy $a = \"false no memory\\nabort at (353|705)\\ncur value: nil\\n1st value: \" . (\" hello\" x 10) . \"1\\n2nd value: \" . (\" hello\" x 10) . \"2\\n\";\n[qr/$a/, qr/$a/]\n--- no_error_log\n[error]\n\n\n\n=== TEST 59: safe_add\n--- http_config\n    lua_shared_dict dogs 100k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local i = 0\n            while i < 1000 do\n                i = i + 1\n                local val = string.rep(\" hello\", 10) .. i\n                local res, err = dogs:safe_add(\"key_\" .. i, val)\n                if not res then\n                    ngx.say(res, \" \", err)\n                    break\n                end\n            end\n            ngx.say(\"abort at \", i)\n            ngx.say(\"cur value: \", dogs:get(\"key_\" .. i))\n            if i > 1 then\n                ngx.say(\"1st value: \", dogs:get(\"key_1\"))\n            end\n            if i > 2 then\n                ngx.say(\"2nd value: \", dogs:get(\"key_2\"))\n            end\n        ';\n    }\n--- pipelined_requests eval\n[\"GET /test\", \"GET /test\"]\n--- response_body eval\nmy $a = \"false no memory\\nabort at (353|705)\\ncur value: nil\\n1st value: \" . (\" hello\" x 10) . \"1\\n2nd value: \" . (\" hello\" x 10) . \"2\\n\";\n[qr/$a/,\nq{false exists\nabort at 1\ncur value:  hello hello hello hello hello hello hello hello hello hello1\n}\n]\n--- no_error_log\n[error]\n\n\n\n=== TEST 60: get_stale: expired entries can still be fetched\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32, 0.01)\n            dogs:set(\"blah\", 33, 0.3)\n            ngx.sleep(0.02)\n            local val, flags, stale = dogs:get_stale(\"foo\")\n            ngx.say(val, \", \", flags, \", \", stale)\n            local val, flags, stale = dogs:get_stale(\"blah\")\n            ngx.say(val, \", \", flags, \", \", stale)\n        ';\n    }\n--- request\nGET /test\n--- response_body\n32, nil, true\n33, nil, false\n--- no_error_log\n[error]\n\n\n\n=== TEST 61: set nil key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:set(nil, 32)\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot ok: nil key\n--- no_error_log\n[error]\n\n\n\n=== TEST 62: set bad zone argument\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs.set(nil, \"foo\", 32)\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad \"zone\" argument\n\n\n\n=== TEST 63: set empty string keys\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:set(\"\", 32)\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot ok: empty key\n--- no_error_log\n[error]\n\n\n\n=== TEST 64: get bad zone argument\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs.get(nil, \"foo\")\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad \"zone\" argument\n\n\n\n=== TEST 65: get nil key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:get(nil)\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot ok: nil key\n--- no_error_log\n[error]\n\n\n\n=== TEST 66: get empty key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:get(\"\")\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot ok: empty key\n--- no_error_log\n[error]\n\n\n\n=== TEST 67: get a too-long key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:get(string.rep(\"a\", 65536))\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot ok: key too long\n--- no_error_log\n[error]\n\n\n\n=== TEST 68: set & get large values\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:set(\"foo\", string.rep(\"helloworld\", 1024))\n            if not ok then\n                ngx.say(\"set not ok: \", err)\n                return\n            end\n            ngx.say(\"set ok\")\n\n            local data, err = dogs:get(\"foo\")\n            if data == nil and err then\n                ngx.say(\"get not ok: \", err)\n                return\n            end\n            ngx.say(\"get ok: \", #data)\n\n        ';\n    }\n--- request\nGET /test\n--- response_body\nset ok\nget ok: 10240\n--- no_error_log\n[error]\n\n\n\n=== TEST 69: get_stale nil key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:get_stale(nil)\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot ok: nil key\n--- no_error_log\n[error]\n\n\n\n=== TEST 70: get_stale empty key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:get_stale(\"\")\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot ok: empty key\n--- no_error_log\n[error]\n\n\n\n=== TEST 71: get_stale number key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:set(1024, \"hello\")\n            if not ok then\n                ngx.say(\"set not ok: \", err)\n                return\n            end\n            ngx.say(\"set ok\")\n            local data, err = dogs:get_stale(1024)\n            if not ok then\n                ngx.say(\"get_stale not ok: \", err)\n                return\n            end\n            ngx.say(\"get_stale: \", data)\n        ';\n    }\n--- request\nGET /test\n--- response_body\nset ok\nget_stale: hello\n--- no_error_log\n[error]\n\n\n\n=== TEST 72: get_stale a too-long key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:get_stale(string.rep(\"a\", 65536))\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot ok: key too long\n--- no_error_log\n[error]\n\n\n\n=== TEST 73: get_stale a non-existent key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local data, err = dogs:get_stale(\"not_found\")\n            if data == nil and err then\n                ngx.say(\"get not ok: \", err)\n                return\n            end\n            ngx.say(\"get ok: \", data)\n        ';\n    }\n--- request\nGET /test\n--- response_body\nget ok: nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 74: set & get_stale large values\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:set(\"foo\", string.rep(\"helloworld\", 1024))\n            if not ok then\n                ngx.say(\"set not ok: \", err)\n                return\n            end\n            ngx.say(\"set ok\")\n\n            local data, err, stale = dogs:get_stale(\"foo\")\n            if data == nil and err then\n                ngx.say(\"get not ok: \", err)\n                return\n            end\n            ngx.say(\"get_stale ok: \", #data, \", stale: \", stale)\n\n        ';\n    }\n--- request\nGET /test\n--- response_body\nset ok\nget_stale ok: 10240, stale: false\n--- no_error_log\n[error]\n\n\n\n=== TEST 75: set & get_stale boolean values (true)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:set(\"foo\", true)\n            if not ok then\n                ngx.say(\"set not ok: \", err)\n                return\n            end\n            ngx.say(\"set ok\")\n\n            local data, err, stale = dogs:get_stale(\"foo\")\n            if data == nil and err then\n                ngx.say(\"get not ok: \", err)\n                return\n            end\n            ngx.say(\"get_stale ok: \", data, \", stale: \", stale)\n\n        ';\n    }\n--- request\nGET /test\n--- response_body\nset ok\nget_stale ok: true, stale: false\n--- no_error_log\n[error]\n\n\n\n=== TEST 76: set & get_stale boolean values (false)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:set(\"foo\", false)\n            if not ok then\n                ngx.say(\"set not ok: \", err)\n                return\n            end\n            ngx.say(\"set ok\")\n\n            local data, err, stale = dogs:get_stale(\"foo\")\n            if data == nil and err then\n                ngx.say(\"get not ok: \", err)\n                return\n            end\n            ngx.say(\"get_stale ok: \", data, \", stale: \", stale)\n\n        ';\n    }\n--- request\nGET /test\n--- response_body\nset ok\nget_stale ok: false, stale: false\n--- no_error_log\n[error]\n\n\n\n=== TEST 77: set & get_stale with a flag\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:set(\"foo\", false, 0, 325)\n            if not ok then\n                ngx.say(\"set not ok: \", err)\n                return\n            end\n            ngx.say(\"set ok\")\n\n            local data, err, stale = dogs:get_stale(\"foo\")\n            if data == nil and err then\n                ngx.say(\"get not ok: \", err)\n                return\n            end\n            local flags = err\n            ngx.say(\"get_stale ok: \", data, \", flags: \", flags,\n                    \", stale: \", stale)\n\n        ';\n    }\n--- request\nGET /test\n--- response_body\nset ok\nget_stale ok: false, flags: 325, stale: false\n--- no_error_log\n[error]\n\n\n\n=== TEST 78: incr nil key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:incr(nil, 32)\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot ok: nil key\n--- no_error_log\n[error]\n\n\n\n=== TEST 79: incr bad zone argument\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs.incr(nil, \"foo\", 32)\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad \"zone\" argument\n\n\n\n=== TEST 80: incr empty string keys\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:incr(\"\", 32)\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot ok: empty key\n--- no_error_log\n[error]\n\n\n\n=== TEST 81: incr too long key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local key = string.rep(\"a\", 65536)\n            local ok, err = dogs:incr(key, 32)\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot ok: key too long\n--- no_error_log\n[error]\n\n\n\n=== TEST 82: incr number key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local key = 56\n            local ok, err = dogs:set(key, 1)\n            if not ok then\n                ngx.say(\"set not ok: \", err)\n                return\n            end\n            ngx.say(\"set ok\")\n            ok, err = dogs:incr(key, 32)\n            if not ok then\n                ngx.say(\"incr not ok: \", err)\n                return\n            end\n            ngx.say(\"incr ok\")\n            local data, err = dogs:get(key)\n            if data == nil and err then\n                ngx.say(\"get not ok: \", err)\n                return\n            end\n            local flags = err\n            ngx.say(\"get ok: \", data, \", flags: \", flags)\n\n        ';\n    }\n--- request\nGET /test\n--- response_body\nset ok\nincr ok\nget ok: 33, flags: nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 83: incr a number-like string key\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local key = 56\n            local ok, err = dogs:set(key, 1)\n            if not ok then\n                ngx.say(\"set not ok: \", err)\n                return\n            end\n            ngx.say(\"set ok\")\n            ok, err = dogs:incr(key, \"32\")\n            if not ok then\n                ngx.say(\"incr not ok: \", err)\n                return\n            end\n            ngx.say(\"incr ok\")\n            local data, err = dogs:get(key)\n            if data == nil and err then\n                ngx.say(\"get not ok: \", err)\n                return\n            end\n            local flags = err\n            ngx.say(\"get ok: \", data, \", flags: \", flags)\n\n        ';\n    }\n--- request\nGET /test\n--- response_body\nset ok\nincr ok\nget ok: 33, flags: nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 84: add nil values\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local ok, err = dogs:add(\"foo\", nil)\n            if not ok then\n                ngx.say(\"not ok: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot ok: attempt to add or replace nil values\n--- no_error_log\n[error]\n\n\n\n=== TEST 85: replace key with exptime\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 2, 0)\n            dogs:replace(\"foo\", 32, 0.01)\n            local data = dogs:get(\"foo\")\n            ngx.say(\"get foo: \", data)\n            ngx.location.capture(\"/sleep/0.02\")\n            local res, err, forcible = dogs:replace(\"foo\", 10502)\n            ngx.say(\"replace: \", res, \" \", err, \" \", forcible)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        ';\n    }\n    location ~ ^/sleep/(.+) {\n        echo_sleep $1;\n    }\n--- request\nGET /test\n--- response_body\nget foo: 32\nreplace: false not found false\nfoo = nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 86: the lightuserdata ngx.null has no methods of shared dicts.\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local lightuserdata = ngx.null\n            lightuserdata:set(\"foo\", 1)\n        ';\n    }\n--- request\nGET /test\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- grep_error_log chop\nattempt to index local 'lightuserdata' (a userdata value)\n--- grep_error_log_out\nattempt to index local 'lightuserdata' (a userdata value)\n--- error_log\n[error]\n--- no_error_log\nbad \"zone\" argument\n\n\n\n=== TEST 87: set bad zone table\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs.set({1}, \"foo\", 1)\n        ';\n    }\n--- request\nGET /test\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad \"zone\" argument\n\n\n\n=== TEST 88: get bad zone table\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs.get({1}, \"foo\")\n        ';\n    }\n--- request\nGET /test\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad \"zone\" argument\n\n\n\n=== TEST 89: incr bad zone table\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs.incr({1}, \"foo\", 32)\n        ';\n    }\n--- request\nGET /test\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\n\n\n\n=== TEST 90: check the type of the shdict object\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            ngx.say(\"type: \", type(ngx.shared.dogs))\n        ';\n    }\n--- request\nGET /test\n--- response_body\ntype: table\n--- no_error_log\n[error]\n\n\n\n=== TEST 91: dogs, cat mixing\n--- http_config\n    lua_shared_dict dogs 1m;\n    lua_shared_dict cats 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            dogs:set(\"bah\", 10502)\n            local val = dogs:get(\"foo\")\n            ngx.say(val, \" \", type(val))\n            val = dogs:get(\"bah\")\n            ngx.say(val, \" \", type(val))\n\n            local cats = ngx.shared.cats\n            val = cats:get(\"foo\")\n            ngx.say(val or \"nil\")\n            val = cats:get(\"bah\")\n            ngx.say(val or \"nil\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\n32 number\n10502 number\nnil\nnil\n--- no_error_log\n[error]\n\n\n\n=== TEST 92: invalid expire time\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32, -1)\n        ';\n    }\n--- request\nGET /test\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad \"exptime\" argument\n\n\n\n=== TEST 93: duplicate zones\n--- http_config\n    lua_shared_dict dogs 1m;\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            ngx.say(\"error\")\n        ';\n    }\n--- request\n    GET /test\n--- request_body_unlike\nerror\n--- must_die\n--- error_log\nlua_shared_dict \"dogs\" is already defined as \"dogs\"\n--- error_log\n[emerg]\n"
  },
  {
    "path": "t/044-req-body.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4 + 58);\n\n#no_diff();\nno_long_string();\n#master_on();\n#workers(2);\nrun_tests();\n\n__DATA__\n\n=== TEST 1: read buffered body\n--- config\n    location = /test {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_body)\n        ';\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nhello, world\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 2: read buffered body (timed out)\n--- config\n    client_body_timeout 1ms;\n    location = /test {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_body)\n        ';\n    }\n--- raw_request eval\n\"POST /test HTTP/1.1\\r\nHost: localhost\\r\nContent-Length: 100\\r\nConnection: close\\r\n\\r\nhello, world\"\n--- response_body:\n--- error_code_like: ^(?:500)?$\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 3: read buffered body and then subrequest\n--- config\n    location /foo {\n        echo -n foo;\n    }\n    location = /test {\n        content_by_lua '\n            ngx.req.read_body()\n            local res = ngx.location.capture(\"/foo\");\n            ngx.say(ngx.var.request_body)\n            ngx.say(\"sub: \", res.body)\n        ';\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nhello, world\nsub: foo\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 4: first subrequest and then read buffered body\n--- config\n    location /foo {\n        echo -n foo;\n    }\n    location = /test {\n        content_by_lua '\n            local res = ngx.location.capture(\"/foo\");\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_body)\n            ngx.say(\"sub: \", res.body)\n        ';\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nhello, world\nsub: foo\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 5: discard body\n--- config\n    location = /foo {\n        content_by_lua '\n            ngx.req.discard_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n        ';\n    }\n    location = /bar {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n        ';\n\n    }\n--- pipelined_requests eval\n[\"POST /foo\nhello, world\",\n\"POST /bar\nhiya, world\"]\n--- response_body eval\n[\"body: nil\\n\",\n\"body: hiya, world\\n\"]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 6: not discard body (content_by_lua falls through)\n--- config\n    location = /foo {\n        content_by_lua '\n            -- ngx.req.discard_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n        ';\n    }\n    location = /bar {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /foo\nhello, world\",\n\"POST /bar\nhiya, world\"]\n--- response_body eval\n[\"body: nil\\n\",\n\"body: hiya, world\\n\",\n]\n--- error_code eval\n[200, 200]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 7: read buffered body and retrieve the data\n--- config\n    location = /test {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.req.get_body_data())\n        ';\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nhello, world\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 8: read buffered body to file and call get_body_data\n--- config\n    client_body_in_file_only on;\n    location = /test {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.req.get_body_data())\n        ';\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nnil\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 9: read buffered body to file and call get_body_file\n--- config\n    client_body_in_file_only on;\n    location = /test {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.req.get_body_file())\n        ';\n    }\n--- request\nPOST /test\nhello, world\n--- response_body_like: client_body_temp/\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 10: read buffered body to memory and retrieve the file\n--- config\n    location = /test {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.req.get_body_file())\n        ';\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nnil\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 11: read buffered body to memory and reset it with data in memory\n--- config\n    location = /test {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.req.set_body_data(\"hiya, dear\")\n            ngx.say(ngx.req.get_body_data())\n            ngx.say(ngx.var.request_body)\n            ngx.say(ngx.var.echo_request_body)\n        ';\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nhiya, dear\nhiya, dear\nhiya, dear\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 12: read body to file and then override it with data in memory\n--- config\n    client_body_in_file_only on;\n\n    location = /test {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.req.set_body_data(\"hello, baby\")\n            ngx.say(ngx.req.get_body_data())\n            ngx.say(ngx.var.request_body)\n        ';\n    }\n--- request\nPOST /test\nyeah\n--- response_body\nhello, baby\nhello, baby\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 13: do not read the current request body but replace it with our own in memory\n--- config\n    client_body_in_file_only on;\n\n    location = /test {\n        content_by_lua '\n            ngx.req.set_body_data(\"hello, baby\")\n            ngx.say(ngx.req.get_body_data())\n            ngx.say(ngx.var.request_body)\n            -- ngx.location.capture(\"/sleep\")\n        ';\n    }\n    location = /sleep {\n        echo_sleep 0.5;\n    }\n--- request\nPOST /test\nyeah\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/lua entry thread aborted: runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):2: request body not read yet/\n--- no_error_log\n[alert]\n\n\n\n=== TEST 14: read buffered body to file and reset it to a new file\n--- config\n\n    location = /test {\n        client_body_in_file_only on;\n        set $old '';\n        set $new '';\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.var.old = ngx.req.get_body_file()\n            ngx.req.set_body_file(ngx.var.realpath_root .. \"/a.txt\")\n            ngx.var.new = ngx.req.get_body_file()\n        ';\n        #echo_request_body;\n        proxy_pass http://127.0.0.1:$server_port/echo;\n        #proxy_pass http://127.0.0.1:7890/echo;\n        add_header X-Old $old;\n        add_header X-New $new;\n    }\n    location /echo {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request\nPOST /test\nhello, world\n--- user_files\n>>> a.txt\nWill you change this world?\n--- raw_response_headers_like eval\nmy $headers;\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $headers = qr#x-old: \\S+/client_body_temp/\\d+\\r\n.*?x-new: \\S+/html/a\\.txt\\r#;\n} else {\n    $headers = qr#X-Old: \\S+/client_body_temp/\\d+\\r\n.*?X-New: \\S+/html/a\\.txt\\r#;\n}\n\n$headers;\n--- response_body\nWill you change this world?\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 15: read buffered body to file and reset it to a new file\n--- config\n    location = /test {\n        client_body_in_file_only on;\n        set $old '';\n        set $new '';\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.var.old = ngx.req.get_body_file() or \"\"\n            ngx.req.set_body_file(ngx.var.realpath_root .. \"/a.txt\")\n            ngx.var.new = ngx.req.get_body_file()\n        ';\n        #echo_request_body;\n        proxy_pass http://127.0.0.1:$server_port/echo;\n        #proxy_pass http://127.0.0.1:7890/echo;\n        add_header X-Old $old;\n        add_header X-New $new;\n    }\n    location /echo {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request\nPOST /test\nhello, world!\n--- user_files\n>>> a.txt\nWill you change this world?\n--- raw_response_headers_like eval\nmy $headers;\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $headers = qr#x-old: \\S+/client_body_temp/\\d+\\r\n.*?x-new: \\S+/html/a\\.txt\\r#;\n} else {\n    $headers = qr#X-Old: \\S+/client_body_temp/\\d+\\r\n.*?X-New: \\S+/html/a\\.txt\\r#;\n}\n\n$headers;\n--- response_body\nWill you change this world?\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 16: read buffered body to file and reset it to a new file (auto-clean)\n--- config\n    client_body_in_file_only on;\n\n    location = /test {\n        set $old '';\n        set $new '';\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.var.old = ngx.req.get_body_file()\n            local a_file = ngx.var.realpath_root .. \"/a.txt\"\n            ngx.req.set_body_file(a_file, true)\n            local b_file = ngx.var.realpath_root .. \"/b.txt\"\n            ngx.req.set_body_file(b_file, true)\n            ngx.say(\"a.txt exists: \", io.open(a_file) and \"yes\" or \"no\")\n            ngx.say(\"b.txt exists: \", io.open(b_file) and \"yes\" or \"no\")\n        ';\n    }\n    location /echo {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request\nPOST /test\nhello, world\n--- user_files\n>>> a.txt\nWill you change this world?\n>>> b.txt\nSure I will!\n--- response_body\na.txt exists: no\nb.txt exists: yes\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 17: read buffered body to memory and reset it to a new file (auto-clean)\n--- config\n    client_body_in_file_only off;\n\n    location = /test {\n        set $old '';\n        set $new '';\n        rewrite_by_lua '\n            ngx.req.read_body()\n            local a_file = ngx.var.realpath_root .. \"/a.txt\"\n            ngx.req.set_body_file(a_file, true)\n        ';\n        echo_request_body;\n    }\n    location /echo {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- pipelined_requests eval\n[\"POST /test\nhello, world\",\n\"POST /test\nhey, you\"]\n--- user_files\n>>> a.txt\nWill you change this world?\n--- response_body eval\n[\"Will you change this world?\\n\",\nqr/500 Internal Server Error/]\n--- error_code eval\n[200, 500]\n--- no_error_log\n[alert]\n\n\n\n=== TEST 18: read buffered body to memory and reset it to a new file (no auto-clean)\n--- config\n    client_body_in_file_only off;\n\n    location = /test {\n        set $old '';\n        set $new '';\n        rewrite_by_lua '\n            ngx.req.read_body()\n            local a_file = ngx.var.realpath_root .. \"/a.txt\"\n            ngx.req.set_body_file(a_file, false)\n        ';\n        echo_request_body;\n    }\n    location /echo {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- pipelined_requests eval\n[\"POST /test\nhello, world\",\n\"POST /test\nhey, you\"]\n--- user_files\n>>> a.txt\nWill you change this world?\n--- response_body eval\n[\"Will you change this world?\\n\",\n\"Will you change this world?\\n\"]\n--- error_code eval\n[200, 200]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 19: request body discarded and reset it to a new file (auto-clean)\n--- config\n    client_body_in_file_only off;\n    client_header_buffer_size 80;\n\n    location = /test {\n        set $old '';\n        set $new '';\n        rewrite_by_lua '\n            ngx.req.discard_body()\n            local a_file = ngx.var.realpath_root .. \"/a.txt\"\n            ngx.req.set_body_file(a_file, false)\n        ';\n        echo_request_body;\n    }\n    location /echo {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request\nPOST /test\nhello, world\n\n--- user_files\n>>> a.txt\nWill you change this world?\n\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- no_error_log\n[alert]\n\n\n\n=== TEST 20: no request body and reset it to a new file (no auto-clean)\n--- config\n    client_body_in_file_only off;\n\n    location = /test {\n        set $old '';\n        set $new '';\n        rewrite_by_lua '\n            local a_file = ngx.var.realpath_root .. \"/a.txt\"\n            ngx.req.set_body_file(a_file, true)\n        ';\n        echo_request_body;\n    }\n    location /echo {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request\nPOST /test\nhello, world\n\n--- user_files\n>>> a.txt\nWill you change this world?\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/lua entry thread aborted: runtime error: rewrite_by_lua\\(nginx\\.conf:\\d+\\):3: request body not read yet/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 21: read buffered body to memory and reset it with data in memory + proxy\n--- config\n    location = /test {\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.req.set_body_data(\"hiya, dear dear friend!\")\n        ';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n    location = /echo {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request\nPOST /test\nhello, world\n--- response_body chomp\nhiya, dear dear friend!\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 22: discard request body and reset it to a new file (no auto-clean)\n--- config\n    client_body_in_file_only off;\n\n    location = /test {\n        set $old '';\n        set $new '';\n        rewrite_by_lua '\n            ngx.req.discard_body()\n            local a_file = ngx.var.realpath_root .. \"/a.txt\"\n            ngx.req.set_body_file(a_file, true)\n        ';\n        echo_request_body;\n    }\n    location /echo {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request\nPOST /test\nhello, world\n\n--- user_files\n>>> a.txt\nWill you change this world?\n\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- no_error_log\n[alert]\n\n\n\n=== TEST 23: discard body and then read\n--- config\n    location = /test {\n        content_by_lua '\n            ngx.req.discard_body()\n            ngx.req.read_body()\n            ngx.print(ngx.req.get_body_data())\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /test\nhello, world\",\n\"POST /test\nhello, world\"]\n--- response_body eval\n[\"nil\",\"nil\"]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 24: set empty request body in memory\n--- config\n    location = /test {\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.req.set_body_data(\"\")\n        ';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n    location = /echo {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(\"body: [\", ngx.req.get_body_data(), \"]\")\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /test\nhello, world\",\n\"POST /test\nhello, world\"]\n--- response_body eval\n[\"body: [nil]\\n\",\"body: [nil]\\n\"]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 25: set empty request body in file\n--- config\n    location = /test {\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.req.set_body_file(ngx.var.realpath_root .. \"/a.txt\")\n        ';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n    location = /echo {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(\"body: [\", ngx.req.get_body_data(), \"]\")\n        ';\n    }\n--- user_files\n>>> a.txt\n--- pipelined_requests eval\n[\"POST /test\nhello, world\",\n\"POST /test\nhello, world\"]\n--- response_body eval\n[\"body: [nil]\\n\",\"body: [nil]\\n\"]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 26: read and set body\n--- config\n    location /test {\n        lua_need_request_body on;\n        access_by_lua_file html/myscript.lua;\n        echo_request_body;\n    }\n--- user_files\n>>> myscript.lua\n    local data, err = ngx.req.get_post_args()\n    if err then\n        ngx.log(ngx.ERR, \"err: \", err)\n        return ngx.exit(500)\n    end\n\n    local data2 = {}\n    for k, v in pairs(data) do\n        if type(v) == \"table\" then\n            for i, val in ipairs(v) do\n                local s = ngx.escape_uri(string.upper(k)) .. '='\n                        .. ngx.escape_uri(string.upper(val))\n                table.insert(data2, s)\n            end\n        else\n            local s = ngx.escape_uri(string.upper(k)) .. '='\n                    .. ngx.escape_uri(string.upper(v))\n            table.insert(data2, s)\n        end\n    end\n    ngx.req.set_body_data(table.concat(data2, \"&\"))\n--- request\nPOST /test\na=1&a=2&b=hello&c=world\n--- response_body\nB=HELLO&A=1&A=2&C=WORLD\n--- no_error_log\n[error]\n--- SKIP\n\n\n\n=== TEST 27: read buffered body to memory and reset it with data in memory + proxy twice\n--- config\n    location = /test {\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.req.set_body_data(\"hiya, dear dear friend!\")\n            ngx.req.set_body_data(\"howdy, my dear little sister!\")\n        ';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n    location = /echo {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request\nPOST /test\nhello, world\n--- response_body chomp\nhowdy, my dear little sister!\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 28: read buffered body to memory and reset it with data in memory and then reset it to file\n--- config\n    location = /test {\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.req.set_body_data(\"hiya, dear dear friend!\")\n            ngx.req.set_body_file(ngx.var.realpath_root .. \"/a.txt\")\n        ';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n    location = /echo {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- user_files\n>>> a.txt\nhowdy, my dear little sister!\n--- request\nPOST /test\nhello, world\n--- response_body\nhowdy, my dear little sister!\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 29: read buffered body to memory and reset it with empty string + proxy twice\n--- config\n    location = /test {\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.req.set_body_data(\"hiya, dear dear friend!\")\n            ngx.req.set_body_data(\"\")\n        ';\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n    location = /echo {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request\nPOST /test\nhello, world\n--- response_body chomp\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 30: multi-buffer request body\n--- config\n    location /foo {\n        default_type text/css;\n        srcache_store POST /store;\n\n        echo hello;\n        echo world;\n    }\n\n    location /store {\n        content_by_lua '\n            local body = ngx.req.get_body_data()\n            ngx.log(ngx.WARN, \"srcache_store: request body len: \", #body)\n        ';\n    }\n--- request\nGET /foo\n--- response_body\nhello\nworld\n--- error_log\nsrcache_store: request body len: 55\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 31: init & append & finish (just in buffer)\n--- config\n    location /t {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.req.init_body(4)\n            ngx.req.append_body(\"h\")\n            ngx.req.append_body(\"ell\")\n            ngx.req.finish_body()\n\n            ngx.say(\"content length: \", ngx.var.http_content_length)\n\n            local data = ngx.req.get_body_data()\n            ngx.say(\"body: \", data)\n\n        ';\n    }\n--- request\n    GET /t\n--- stap2\nF(ngx_http_lua_write_request_body) {\n    b = ngx_chain_buf($body)\n    println(\"buf: \", b,\n        \", in-mem: \", ngx_buf_in_memory(b),\n        \", size: \", ngx_buf_size(b),\n        \", data: \", ngx_buf_data(b))\n}\n--- response_body\ncontent length: 4\nbody: hell\n--- no_error_log\n[error]\n[alert]\n--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 32: init & append & finish (exceeding the buffer size)\n--- config\n    location /t {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.req.init_body(4)\n            ngx.req.append_body(\"h\")\n            ngx.req.append_body(\"ell\")\n            ngx.req.append_body(\"o\")\n            ngx.req.finish_body()\n\n            ngx.say(\"content length: \", ngx.var.http_content_length)\n\n            local data = ngx.req.get_body_data()\n            ngx.say(\"body: \", data)\n\n            local file = ngx.req.get_body_file()\n            if not file then\n                ngx.say(\"body file: \", file)\n                return\n            end\n\n            local f, err = io.open(file, \"r\")\n            if not f then\n                ngx.say(\"failed to open file: \", err)\n                return\n            end\n\n            local data = f:read(\"*a\")\n            f:close()\n            ngx.say(\"body file: \", data)\n        ';\n    }\n--- request\n    GET /t\n--- stap2\nF(ngx_http_lua_write_request_body) {\n    b = ngx_chain_buf($body)\n    println(\"buf: \", b,\n        \", in-mem: \", ngx_buf_in_memory(b),\n        \", size: \", ngx_buf_size(b),\n        \", data: \", ngx_buf_data(b))\n}\nF(ngx_open_tempfile) {\n    println(\"open temp file \", user_string($name), \", persist: \", $persistent)\n}\nF(ngx_pool_delete_file) {\n    println(\"delete \", ngx_pool_cleanup_file_name($data))\n}\n--- response_body\ncontent length: 5\nbody: nil\nbody file: hello\n--- no_error_log\n[error]\n[alert]\n--- error_log\na client request body is buffered to a temporary file\n--- skip_eval: 5:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 33: init & append & finish (use default buffer size) - body not read yet\n--- config\n    location /t {\n        client_body_buffer_size 4;\n        content_by_lua '\n            ngx.req.init_body()\n            ngx.req.append_body(\"h\")\n            ngx.req.append_body(\"ell\")\n            ngx.req.finish_body()\n\n            ngx.say(\"content length: \", ngx.var.http_content_length)\n\n            local data = ngx.req.get_body_data()\n            ngx.say(\"body: \", data)\n\n        ';\n    }\n--- request\n    GET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/lua entry thread aborted: runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):2: request body not read yet/\n--- no_error_log\n[alert]\n--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 34: init & append & finish (use default buffer size)\n--- config\n    location /t {\n        client_body_buffer_size 4;\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.req.init_body()\n            ngx.req.append_body(\"h\")\n            ngx.req.append_body(\"ell\")\n            ngx.req.finish_body()\n\n            ngx.say(\"content length: \", ngx.var.http_content_length)\n\n            local data = ngx.req.get_body_data()\n            ngx.say(\"body: \", data)\n\n        ';\n    }\n--- request\n    GET /t\n--- response_body\ncontent length: 4\nbody: hell\n--- no_error_log\n[error]\n[alert]\na client request body is buffered to a temporary file\n--- skip_eval: 5:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 35: init & append & finish (exceeding the buffer size, proxy)\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.req.init_body(4)\n            ngx.req.append_body(\"h\")\n            ngx.req.append_body(\"ell\")\n            ngx.req.append_body(\"o\\\\n\")\n            ngx.req.finish_body()\n        ';\n\n        proxy_pass http://127.0.0.1:$server_port/back;\n    }\n\n    location = /back {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request\nPOST /t\ni do like the sky\n\n--- stap\nglobal valid = 0\n\nF(ngx_http_handler) { valid = 1  }\n\nprobe syscall.unlink {\n    if (valid && pid() == target()) {\n        println(name, \"(\", argstr, \")\")\n    }\n}\n\n--- stap_out_like chop\n^unlink\\(\".*?client_body_temp/\\d+\"\\)$\n--- response_body\nhello\n--- no_error_log\n[error]\n[alert]\n--- error_log\na client request body is buffered to a temporary file\n\n\n\n=== TEST 36: init & append & finish (just in buffer, proxy)\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.req.init_body(4)\n            ngx.req.append_body(\"h\")\n            ngx.req.append_body(\"ell\")\n            ngx.req.finish_body()\n        ';\n\n        proxy_pass http://127.0.0.1:$server_port/back;\n    }\n\n    location = /back {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request\nPOST /t\ni do like the sky\n--- response_body chop\nhell\n--- no_error_log\n[error]\n[alert]\na client request body is buffered to a temporary file\n\n\n\n=== TEST 37: init & append & finish (exceeding buffer size, discard on-disk buffer)\n--- config\n    client_header_buffer_size 100;\n    location /t {\n        client_body_buffer_size 4;\n\n        content_by_lua '\n            ngx.req.read_body()\n\n            -- ngx.say(\"original body: \", ngx.req.get_body_data())\n            -- ngx.say(\"original body file: \", ngx.req.get_body_file())\n\n            ngx.req.init_body(4)\n            ngx.req.append_body(\"h\")\n            ngx.req.append_body(\"ell\")\n            ngx.req.append_body(\"o\")\n            ngx.req.finish_body()\n\n            ngx.say(\"content length: \", ngx.var.http_content_length)\n\n            local data = ngx.req.get_body_data()\n            ngx.say(\"body: \", data)\n\n            local file = ngx.req.get_body_file()\n            if not file then\n                ngx.say(\"body file: \", file)\n                return\n            end\n\n            local f, err = io.open(file, \"r\")\n            if not f then\n                ngx.say(\"failed to open file: \", err)\n                return\n            end\n\n            local data = f:read(\"*a\")\n            f:close()\n            ngx.say(\"body file: \", data)\n        ';\n    }\n--- request eval\n\"POST /t\n\" . (\"howdyworld\" x 15)\n--- stap\n/*\nF(ngx_http_read_client_request_body) { T() }\nM(http-read-body-abort) { println(\"read body aborted: \", user_string($arg2)) }\nM(http-read-req-header-done) { println(\"req header: \", ngx_table_elt_key($arg2), \": \", ngx_table_elt_value($arg2)) }\n#probe syscall.open { if (isinstr(argstr, \"temp\")) { println(name, \": \", argstr) } }\n\nprobe syscall.unlink {\n    println(name, \": \", argstr, \" :\", target(), \" == \", pid(), \": \", execname())\n    system(sprintf(\"ps aux|grep %d|grep -v grep > /dev/stderr\", target()))\n    system(sprintf(\"ps aux|grep %d|grep -v grep  > /dev/stderr\", pid()))\n}\n*/\n\nglobal valid = 0\n\nF(ngx_http_handler) { valid = 1  }\n#F(ngx_http_free_request) { valid = 0 }\n\nprobe syscall.unlink {\n    if (valid && pid() == target()) {\n        println(name, \"(\", argstr, \")\")\n        #print_ubacktrace()\n    }\n}\n\n/*\nprobe syscall.close, syscall.open, syscall.unlink {\n    if (valid && pid() == target()) {\n        print(name, \"(\", argstr, \")\")\n        #print_ubacktrace()\n    }\n}\n\nprobe syscall.close.return, syscall.open.return, syscall.unlink.return {\n    if (valid && pid() == target()) {\n        println(\" = \", retstr)\n    }\n}\n*/\n--- stap_out_like chop\n^unlink\\(\".*?client_body_temp/\\d+\"\\)\nunlink\\(\".*?client_body_temp/\\d+\"\\)$\n--- response_body\ncontent length: 5\nbody: nil\nbody file: hello\n--- no_error_log\n[error]\n[alert]\n--- error_log\na client request body is buffered to a temporary file\n\n\n\n=== TEST 38: ngx.req.socket + init & append & finish (requests)\n--- config\n    location = /t {\n        client_body_buffer_size 1;\n        lua_socket_buffer_size 1;\n        content_by_lua '\n            local sock,err = ngx.req.socket()\n            if not sock then\n                ngx.say(\"failed to get req socket: \", err)\n                return\n            end\n\n            ngx.req.init_body(100)\n\n            while true do\n                local data, err = sock:receive(1)\n                if not data then\n                    if err == \"closed\" then\n                        break\n                    else\n                        ngx.say(\"failed to read body: \", err)\n                        return\n                    end\n                end\n                ngx.req.append_body(data)\n            end\n\n            ngx.req.finish_body()\n\n            ngx.say(\"content length: \", ngx.var.http_content_length)\n\n            local data = ngx.req.get_body_data()\n            ngx.say(\"body: \", data)\n\n        ';\n    }\n--- request\nPOST /t\nhello, my dear friend!\n--- response_body\ncontent length: 22\nbody: hello, my dear friend!\n--- no_error_log\n[error]\n[alert]\na client request body is buffered to a temporary file\n--- skip_eval: 5:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 39: ngx.req.socket + init & append & finish (pipelined requests, small buffer size)\n--- config\n    location = /t {\n        client_body_buffer_size 1;\n        lua_socket_buffer_size 1;\n        content_by_lua '\n            local sock,err = ngx.req.socket()\n            if not sock then\n                ngx.say(\"failed to get req socket: \", err)\n                return\n            end\n\n            ngx.req.init_body(100)\n\n            while true do\n                local data, err = sock:receive(1)\n                if not data then\n                    if err == \"closed\" then\n                        break\n                    else\n                        ngx.say(\"failed to read body: \", err)\n                        return\n                    end\n                end\n                ngx.req.append_body(data)\n            end\n\n            ngx.req.finish_body()\n\n            ngx.say(\"content length: \", ngx.var.http_content_length)\n\n            local data = ngx.req.get_body_data()\n            ngx.say(\"body: \", data)\n\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /t\nhello, my dear friend!\",\n\"POST /t\nblah blah blah\"]\n--- response_body eval\n[\"content length: 22\nbody: hello, my dear friend!\n\",\"content length: 14\nbody: blah blah blah\n\"]\n--- no_error_log\n[error]\n[alert]\n--- no_error_log\na client request body is buffered to a temporary file\n\n\n\n=== TEST 40: ngx.req.socket + init & append & finish (pipelined requests, big buffer size)\n--- config\n    location = /t {\n        client_body_buffer_size 100;\n        lua_socket_buffer_size 100;\n        content_by_lua '\n            local sock,err = ngx.req.socket()\n            if not sock then\n                ngx.say(\"failed to get req socket: \", err)\n                return\n            end\n\n            ngx.req.init_body(100)\n\n            while true do\n                local data, err, partial = sock:receive(100)\n                if not data then\n                    if err == \"closed\" then\n                        ngx.req.append_body(partial)\n                        break\n                    else\n                        ngx.say(\"failed to read body: \", err)\n                        return\n                    end\n                end\n                ngx.req.append_body(data)\n            end\n\n            ngx.req.finish_body()\n\n            ngx.say(\"content length: \", ngx.var.http_content_length)\n\n            local data = ngx.req.get_body_data()\n            ngx.say(\"body: \", data)\n\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /t\nhello, my dear friend!\",\n\"POST /t\nblah blah blah\"]\n--- response_body eval\n[\"content length: 22\nbody: hello, my dear friend!\n\",\"content length: 14\nbody: blah blah blah\n\"]\n--- no_error_log\n[error]\n[alert]\n--- no_error_log\na client request body is buffered to a temporary file\n\n\n\n=== TEST 41: calling ngx.req.socket after ngx.req.read_body\n--- config\n    location = /t {\n        client_body_buffer_size 100;\n        lua_socket_buffer_size 100;\n        content_by_lua '\n            ngx.req.read_body()\n\n            local sock, err = ngx.req.socket()\n            if not sock then\n                ngx.say(\"failed to get req socket: \", err)\n                return\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nPOST /t\nhello, my dear friend!\n--- response_body\nfailed to get req socket: request body already exists\n--- no_error_log\n[error]\n[alert]\na client request body is buffered to a temporary file\n--- skip_eval: 5:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 42: failed to write 100 continue\n--- config\n    location = /test {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.var.request_body)\n        ';\n    }\n--- request\nPOST /test\nhello, world\n--- more_headers\nExpect: 100-Continue\n--- ignore_response\n--- no_error_log\n[alert]\n[error]\nhttp finalize request: 500, \"/test?\" a:1, c:0\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 43: chunked support in ngx.req.read_body\n--- config\n    location /t {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.req.get_body_data())\n        ';\n    }\n--- raw_request eval\n\"POST /t HTTP/1.1\\r\nHost: localhost\\r\nTransfer-Encoding: chunked\\r\nConnection: close\\r\n\\r\n5\\r\nhello\\r\n1\\r\n,\\r\n1\\r\n \\r\n5\\r\nworld\\r\n0\\r\n\\r\n\"\n\n--- response_body\nhello, world\n--- no_error_log\n[error]\n[alert]\n--- skip_nginx: 4: <1.3.9\n\n\n\n=== TEST 44: zero size request body and reset it to a new file\n--- config\n    location = /test {\n        client_body_in_file_only on;\n        set $old '';\n        set $new '';\n        rewrite_by_lua '\n            ngx.req.read_body()\n            ngx.req.set_body_file(ngx.var.realpath_root .. \"/a.txt\")\n            ngx.var.new = ngx.req.get_body_file()\n        ';\n        #echo_request_body;\n        proxy_pass http://127.0.0.1:$server_port/echo;\n        #proxy_pass http://127.0.0.1:7890/echo;\n        add_header X-Old $old;\n        add_header X-New $new;\n    }\n    location /echo {\n        echo_read_request_body;\n        echo_request_body;\n    }\n--- request\nPOST /test\n--- user_files\n>>> a.txt\nWill you change this world?\n\n--- stap\nprobe syscall.fcntl {\n    O_DIRECT = 0x4000\n    if (pid() == target() && ($arg & O_DIRECT)) {\n        println(\"fcntl(O_DIRECT)\")\n    }\n}\n--- stap_out_unlike\nfcntl\\(O_DIRECT\\)\n\n--- raw_response_headers_like eval\nmy $headers;\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $headers = qr#.*?x-new: \\S+/html/a\\.txt\\r#;\n} else {\n    $headers = qr#.*?X-New: \\S+/html/a\\.txt\\r#;\n}\n\n$headers;\n--- response_body\nWill you change this world?\n--- no_error_log\n[error]\n[alert]\n--- skip_eval: 6:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 45: not discard body (content_by_lua exit 200)\n--- config\n    location = /foo {\n        content_by_lua '\n            -- ngx.req.discard_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n            ngx.exit(200)\n        ';\n    }\n    location = /bar {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /foo\nhello, world\",\n\"POST /bar\nhiya, world\"]\n--- response_body eval\n[\"body: nil\\n\",\n\"body: hiya, world\\n\",\n]\n--- error_code eval\n[200, 200]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 46: not discard body (content_by_lua exit 201)\n--- config\n    location = /foo {\n        content_by_lua '\n            -- ngx.req.discard_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n            ngx.exit(201)\n        ';\n    }\n    location = /bar {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /foo\nhello, world\",\n\"POST /bar\nhiya, world\"]\n--- response_body eval\n[\"body: nil\\n\",\n\"body: hiya, world\\n\",\n]\n--- error_code eval\n[200, 200]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 47: not discard body (content_by_lua exit 302)\n--- config\n    location = /foo {\n        content_by_lua '\n            -- ngx.req.discard_body()\n            -- ngx.say(\"body: \", ngx.var.request_body)\n            ngx.redirect(\"/blah\")\n        ';\n    }\n    location = /bar {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(\"body: \", ngx.var.request_body)\n        ';\n    }\n--- pipelined_requests eval\n[\"POST /foo\nhello, world\",\n\"POST /bar\nhiya, world\"]\n--- response_body eval\n[qr/302 Found/,\n\"body: hiya, world\\n\",\n]\n--- error_code eval\n[302, 200]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 48: not discard body (custom error page)\n--- config\n    error_page 404 = /err;\n\n    location = /foo {\n        content_by_lua '\n            ngx.exit(404)\n        ';\n    }\n    location = /err {\n        content_by_lua 'ngx.say(\"error\")';\n    }\n--- pipelined_requests eval\n[\"POST /foo\nhello, world\",\n\"POST /foo\nhiya, world\"]\n--- response_body eval\n[\"error\\n\",\n\"error\\n\",\n]\n--- error_code eval\n[404, 404]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 49: get body data at log phase\n--- config\n    location = /test {\n        content_by_lua_block {\n            ngx.req.read_body()\n            ngx.say(ngx.req.get_body_data())\n        }\n        log_by_lua_block {\n            ngx.log(ngx.WARN, \"request body:\", ngx.req.get_body_data())\n        }\n    }\n--- request\nPOST /test\nhello, world\n--- response_body\nhello, world\n--- error_log\nrequest body:hello, world\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 50: init & append & finish (content_length = 0)\n--- config\n    location /t {\n        content_by_lua '\n            local old_http_content_length = ngx.var.http_content_length\n\n            ngx.req.read_body()\n            ngx.req.init_body()\n            ngx.req.append_body(\"he\")\n            ngx.req.append_body(\"llo\")\n            ngx.req.finish_body()\n\n            ngx.say(\"old content length: \", old_http_content_length)\n\n            local data = ngx.req.get_body_data()\n            local data_file = ngx.req.get_body_file()\n\n            if not data and data_file then\n                ngx.say(\"no data in buf, go to data file\")\n            end\n\n            ngx.say(\"content length: \", ngx.var.http_content_length)\n        ';\n    }\n--- request\n    GET /t\n--- more_headers\nContent-Length: 0\n--- response_body\nold content length: 0\ncontent length: 5\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 51: init & append & finish (init_body(0))\n--- config\n    location /t {\n        content_by_lua '\n            local old_http_content_length = ngx.var.http_content_length\n\n            ngx.req.read_body()\n            ngx.req.init_body(0)\n            ngx.req.append_body(\"he\")\n            ngx.req.append_body(\"llo\")\n            ngx.req.finish_body()\n\n            ngx.say(\"old content length: \", old_http_content_length)\n\n            local data = ngx.req.get_body_data()\n            local data_file = ngx.req.get_body_file()\n\n            if not data and data_file then\n                ngx.say(\"no data in buf, go to data file\")\n            end\n\n            ngx.say(\"content length: \", ngx.var.http_content_length)\n        ';\n    }\n--- request\n    GET /t\n--- more_headers\nContent-Length: 0\n--- response_body\nold content length: 0\nno data in buf, go to data file\ncontent length: 5\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 52: init & append & finish (client_body_buffer_size = 0)\n--- http_config\n    client_body_buffer_size 0;\n--- config\n    location /t {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.req.init_body()\n            ngx.req.append_body(\"he\")\n            ngx.req.append_body(\"llo\")\n            ngx.req.finish_body()\n\n            local data = ngx.req.get_body_data()\n            local data_file = ngx.req.get_body_file()\n\n            if not data and data_file then\n                ngx.say(\"no data in buf, go to data file\")\n            end\n\n            ngx.say(\"content length: \", ngx.var.http_content_length)\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nno data in buf, go to data file\ncontent length: 5\n--- no_error_log\n[error]\n[alert]\n"
  },
  {
    "path": "t/045-ngx-var.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 9);\n\n#no_diff();\n#no_long_string();\n#master_on();\n#workers(2);\nrun_tests();\n\n__DATA__\n\n=== TEST 1: set indexed variables to nil\n--- config\n    location = /test {\n        set $var 32;\n        content_by_lua '\n            ngx.say(\"old: \", ngx.var.var)\n            ngx.var.var = nil\n            ngx.say(\"new: \", ngx.var.var)\n        ';\n    }\n--- request\nGET /test\n--- response_body\nold: 32\nnew: nil\n\n\n\n=== TEST 2: set variables with set_handler to nil\n--- config\n    location = /test {\n        content_by_lua '\n            ngx.say(\"old: \", ngx.var.args)\n            ngx.var.args = nil\n            ngx.say(\"new: \", ngx.var.args)\n        ';\n    }\n--- request\nGET /test?hello=world\n--- response_body\nold: hello=world\nnew: nil\n\n\n\n=== TEST 3: reference nonexistent variable\n--- config\n    location = /test {\n        set $var 32;\n        content_by_lua '\n            ngx.say(\"value: \", ngx.var.notfound)\n        ';\n    }\n--- request\nGET /test\n--- response_body\nvalue: nil\n\n\n\n=== TEST 4: no-hash variables\n--- config\n    location = /test {\n        proxy_pass http://127.0.0.1:$server_port/foo;\n        header_filter_by_lua '\n            ngx.header[\"X-My-Host\"] = ngx.var.proxy_host\n        ';\n    }\n\n    location = /foo {\n        echo foo;\n    }\n--- request\nGET /test\n--- response_headers\nX-My-Host: foo\n--- response_body\nfoo\n--- SKIP\n\n\n\n=== TEST 5: variable name is caseless\n--- config\n    location = /test {\n        set $Var 32;\n        content_by_lua '\n            ngx.say(\"value: \", ngx.var.VAR)\n        ';\n    }\n--- request\nGET /test\n--- response_body\nvalue: 32\n\n\n\n=== TEST 6: true $invalid_referer variable value in Lua\ngithub issue #239\n--- config\n    location = /t {\n        valid_referers www.foo.com;\n        content_by_lua '\n            ngx.say(\"invalid referer: \", ngx.var.invalid_referer)\n            ngx.exit(200)\n        ';\n        #echo $invalid_referer;\n    }\n\n--- request\nGET /t\n--- more_headers\nReferer: http://www.foo.com/\n\n--- response_body\ninvalid referer: \n\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: false $invalid_referer variable value in Lua\ngithub issue #239\n--- config\n    location = /t {\n        valid_referers www.foo.com;\n        content_by_lua '\n            ngx.say(\"invalid referer: \", ngx.var.invalid_referer)\n            ngx.exit(200)\n        ';\n        #echo $invalid_referer;\n    }\n\n--- request\nGET /t\n--- more_headers\nReferer: http://www.bar.com\n\n--- response_body\ninvalid referer: 1\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: $proxy_host & $proxy_port & $proxy_add_x_forwarded_for\n--- config\n    location = /t {\n        proxy_pass http://127.0.0.1:$server_port/back;\n        header_filter_by_lua_block {\n            ngx.header[\"Proxy-Host\"] = ngx.var.proxy_host\n            ngx.header[\"Proxy-Port\"] = ngx.var.proxy_port\n            ngx.header[\"Proxy-Add-X-Forwarded-For\"] = ngx.var.proxy_add_x_forwarded_for\n        }\n    }\n\n    location = /back {\n        echo hello;\n    }\n--- request\nGET /t\n--- raw_response_headers_like eval\nmy $headers;\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $headers =\nqr/proxy-host: 127.0.0.1\\:\\d+\\r\nproxy-port: \\d+\\r\nproxy-add-x-forwarded-for: 127.0.0.1\\r/;\n} else {\n    $headers =\nqr/Proxy-Host: 127.0.0.1\\:\\d+\\r\nProxy-Port: \\d+\\r\nProxy-Add-X-Forwarded-For: 127.0.0.1\\r/;\n}\n\n$headers;\n--- response_body\nhello\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: get a bad variable name\n--- config\n    location = /test {\n        set $true 32;\n        content_by_lua '\n            ngx.say(\"value: \", ngx.var[true])\n        ';\n    }\n--- request\nGET /test\n--- response_body_like: 500 Internal Server Error\n--- error_log\nbad variable name\n--- error_code: 500\n\n\n\n=== TEST 10: set a bad variable name\n--- config\n    location = /test {\n        set $true 32;\n        content_by_lua '\n            ngx.var[true] = 56\n        ';\n    }\n--- request\nGET /test\n--- response_body_like: 500 Internal Server Error\n--- error_log\nbad variable name\n--- error_code: 500\n\n\n\n=== TEST 11: set a variable that is not changeable\n--- config\n    location = /test {\n        content_by_lua '\n            ngx.var.query_string = 56\n        ';\n    }\n--- request\nGET /test?hello\n--- response_body_like: 500 Internal Server Error\n--- error_log\nvariable \"query_string\" not changeable\n--- error_code: 500\n\n\n\n=== TEST 12: get a variable in balancer_by_lua_block\n--- http_config\n    upstream balancer {\n        server 127.0.0.1;\n        balancer_by_lua_block {\n            local balancer = require \"ngx.balancer\"\n            local host = \"127.0.0.1\"\n            local port = ngx.var.port;\n            local ok, err = balancer.set_current_peer(host, port)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set the current peer: \", err)\n                return ngx.exit(500)\n            end\n        }\n    }\n    server {\n        # this is the real entry point\n        listen $TEST_NGINX_RAND_PORT_1;\n        location / {\n            content_by_lua_block{\n                ngx.print(\"this is backend peer $TEST_NGINX_RAND_PORT_1\")\n            }\n        }\n    }\n    server {\n        # this is the real entry point\n        listen $TEST_NGINX_RAND_PORT_2;\n        location / {\n            content_by_lua_block{\n                ngx.print(\"this is backend peer $TEST_NGINX_RAND_PORT_2\")\n            }\n        }\n    }\n--- config\n    location =/balancer {\n        set $port '';\n        set_by_lua_block $port {\n            local args, _ = ngx.req.get_uri_args()\n            local port = args['port']\n            return port\n        }\n        proxy_pass http://balancer;\n    }\n--- pipelined_requests eval\n[\"GET /balancer?port=\\$TEST_NGINX_RAND_PORT_1\", \"GET /balancer?port=\\$TEST_NGINX_RAND_PORT_2\"]\n--- response_body eval\n[\"this is backend peer \\$TEST_NGINX_RAND_PORT_1\", \"this is backend peer \\$TEST_NGINX_RAND_PORT_2\"]\n"
  },
  {
    "path": "t/046-hmac.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 2);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /lua {\n        content_by_lua '\n            local digest = ngx.hmac_sha1(\"thisisverysecretstuff\", \"some string we want to sign\")\n            ngx.say(ngx.encode_base64(digest))\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nR/pvxzHC4NLtj7S+kXFg/NePTmk=\n"
  },
  {
    "path": "t/047-match-jit.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 5);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: matched with j\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]+)\", \"j\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n--- error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\\n\"\n:\n\"pcre JIT compiling result: 1\\n\"\n\n\n\n=== TEST 2: not matched with j\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, world\", \"([0-9]+)\", \"j\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched!\n--- error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\\n\"\n:\n\"pcre JIT compiling result: 1\\n\"\n\n\n\n=== TEST 3: matched with jo\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, 1234\", \"([0-9]+)\", \"jo\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\n1234\n\n--- grep_error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\"\n:\n\"pcre JIT compiling result: 1\"\n\n--- grep_error_log_out eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n[\"pcre2 JIT compiled successfully\\n\", \"\"]\n:\n[\"pcre JIT compiling result: 1\\n\", \"\"]\n\n\n\n=== TEST 4: not matched with jo\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello, world\", \"([0-9]+)\", \"jo\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched!\n\n--- grep_error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\"\n:\n\"pcre JIT compiling result: 1\"\n\n--- grep_error_log_out eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n[\"pcre2 JIT compiled successfully\\n\", \"\"]\n:\n[\"pcre JIT compiling result: 1\\n\", \"\"]\n\n\n\n=== TEST 5: bad pattern\n--- config\n    location /re {\n        content_by_lua '\n            local m, err = ngx.re.match(\"hello\\\\nworld\", \"(abc\", \"j\")\n            if m then\n                ngx.say(m[0])\n\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: just hit match limit\n--- http_config\n    lua_regex_match_limit 2940;\n--- config\n    location /re {\n        content_by_lua_file html/a.lua;\n    }\n\n--- user_files\n>>> a.lua\nlocal re = [==[(?i:([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:=|<=>|r?like|sounds\\s+like|regexp)([\\s'\\\"`´’‘\\(\\)]*)?\\2|([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:!=|<=|>=|<>|<|>|\\^|is\\s+not|not\\s+like|not\\s+regexp)([\\s'\\\"`´’‘\\(\\)]*)?(?!\\6)([\\d\\w]+))]==]\n\nlocal s = string.rep([[ABCDEFG]], 21)\n\nlocal start = ngx.now()\n\nlocal res, err = ngx.re.match(s, re, \"jo\")\n\n--[[\nngx.update_time()\nlocal elapsed = ngx.now() - start\nngx.say(elapsed, \" sec elapsed.\")\n]]\n\nif not res then\n    if err then\n        ngx.say(\"error: \", err)\n        return\n    end\n    ngx.say(\"failed to match\")\n    return\nend\n\n--- request\n    GET /re\n--- response_body eval\n# lua_regex_match_limit uses pcre_extra->match_limit in the PCRE,\n# but PCRE2 replaces this with pcre2_set_match_limit interface,\n# which has different effects.\n$Test::Nginx::Util::PcreVersion == 2 ?\n# PCRE2_ERROR_MATCHLIMIT  (-47)\n\"error: pcre_exec() failed: -47\\n\"\n:\n\"error: pcre_exec() failed: -8\\n\"\n\n\n\n=== TEST 7: just not hit match limit\n--- http_config\n    lua_regex_match_limit 2950;\n--- config\n    location /re {\n        content_by_lua_file html/a.lua;\n    }\n\n--- user_files\n>>> a.lua\nlocal re = [==[(?i:([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:=|<=>|r?like|sounds\\s+like|regexp)([\\s'\\\"`´’‘\\(\\)]*)?\\2|([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:!=|<=|>=|<>|<|>|\\^|is\\s+not|not\\s+like|not\\s+regexp)([\\s'\\\"`´’‘\\(\\)]*)?(?!\\6)([\\d\\w]+))]==]\n\nlocal s = string.rep([[ABCDEFG]], 21)\n\nlocal start = ngx.now()\n\nlocal res, err = ngx.re.match(s, re, \"jo\")\n\n--[[\nngx.update_time()\nlocal elapsed = ngx.now() - start\nngx.say(elapsed, \" sec elapsed.\")\n]]\n\nif not res then\n    if err then\n        ngx.say(\"error: \", err)\n        return\n    end\n    ngx.say(\"failed to match\")\n    return\nend\n\n--- request\n    GET /re\n--- response_body\nfailed to match\n"
  },
  {
    "path": "t/048-match-dfa.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 4);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: matched with d\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\", \"(he|hell)\", \"d\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhell\nnil\nnil\n\n\n\n=== TEST 2: matched with d + o\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\", \"(he|hell)\", \"do\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhell\nnil\nnil\n\n\n\n=== TEST 3: matched with d + j\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\", \"(he|hell)\", \"jd\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhell\n\n\n\n=== TEST 4: not matched with j\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"world\", \"(he|hell)\", \"d\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched!\n\n\n\n=== TEST 5: matched with do\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\", \"he|hell\", \"do\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhell\nnil\nnil\n\n\n\n=== TEST 6: not matched with do\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"world\", \"([0-9]+)\", \"do\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched!\n\n\n\n=== TEST 7: UTF-8 mode without UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"你好\", \".\", \"Ud\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_dfa_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 2000\n\n--- request\n    GET /re\n--- response_body\n你\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: UTF-8 mode with UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"你好\", \".\", \"ud\")\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_dfa_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 0\n\n--- request\n    GET /re\n--- response_body\n你\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: matched with do\n--- config\n    location /re {\n        content_by_lua '\n            local m = ngx.re.match(\"hello\", \"(h)(e)(l)\", \"jo\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n                ngx.say(m[3])\n            else\n                ngx.say(\"not matched!\")\n            end\n            local m = ngx.re.match(\"horld\", \"(h)(e)?(l)?\", \"jo\")\n            if m then\n                ngx.say(m[0])\n                ngx.say(m[1])\n                ngx.say(m[2])\n                ngx.say(m[3])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhel\nh\ne\nl\nh\nh\nfalse\nfalse\n"
  },
  {
    "path": "t/049-gmatch-jit.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 9);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: gmatch matched\n--- config\n    location /re {\n        content_by_lua '\n            for m in ngx.re.gmatch(\"hello, world\", \"[a-z]+\", \"j\") do\n                if m then\n                    ngx.say(m[0])\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nworld\n--- error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\\n\"\n:\n\"pcre JIT compiling result: 1\\n\"\n\n\n\n=== TEST 2: fail to match\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[0-9]\", \"j\")\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnil\nnil\nnil\n--- error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\\n\"\n:\n\"pcre JIT compiling result: 1\\n\"\n\n\n\n=== TEST 3: gmatch matched but no iterate\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[a-z]+\", \"j\")\n            ngx.say(\"done\")\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ndone\n--- error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\\n\"\n:\n\"pcre JIT compiling result: 1\\n\"\n\n\n\n=== TEST 4: gmatch matched but only iterate once and still matches remain\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[a-z]+\", \"j\")\n            local m = it()\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\n--- error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\\n\"\n:\n\"pcre JIT compiling result: 1\\n\"\n\n\n\n=== TEST 5: gmatch matched + o\n--- config\n    location /re {\n        content_by_lua '\n            for m in ngx.re.gmatch(\"hello, world\", \"[a-z]+\", \"jo\") do\n                if m then\n                    ngx.say(m[0])\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nworld\n\n--- grep_error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\"\n:\n\"pcre JIT compiling result: 1\"\n\n--- grep_error_log_out eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n[\"pcre2 JIT compiled successfully\\n\", \"\"]\n:\n[\"pcre JIT compiling result: 1\\n\", \"\"]\n\n\n\n=== TEST 6: fail to match + o\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[0-9]\", \"jo\")\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnil\nnil\nnil\n\n--- grep_error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\"\n:\n\"pcre JIT compiling result: 1\"\n\n--- grep_error_log_out eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n[\"pcre2 JIT compiled successfully\\n\", \"\"]\n:\n[\"pcre JIT compiling result: 1\\n\", \"\"]\n\n\n\n=== TEST 7: gmatch matched but no iterate + o\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[a-z]+\", \"jo\")\n            ngx.say(\"done\")\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ndone\n\n--- grep_error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\"\n:\n\"pcre JIT compiling result: 1\"\n\n--- grep_error_log_out eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n[\"pcre2 JIT compiled successfully\\n\", \"\"]\n:\n[\"pcre JIT compiling result: 1\\n\", \"\"]\n\n\n\n=== TEST 8: gmatch matched but only iterate once and still matches remain + o\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[a-z]+\", \"jo\")\n            local m = it()\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\n\n--- grep_error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\"\n:\n\"pcre JIT compiling result: 1\"\n\n--- grep_error_log_out eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n[\"pcre2 JIT compiled successfully\\n\", \"\"]\n:\n[\"pcre JIT compiling result: 1\\n\", \"\"]\n\n\n\n=== TEST 9: bad pattern\n--- config\n    location /re {\n        content_by_lua '\n            local m, err = ngx.re.gmatch(\"hello\\\\nworld\", \"(abc\", \"j\")\n            if not m then\n                ngx.say(\"error: \", err)\n                return\n            end\n            ngx.say(\"success\")\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/050-gmatch-dfa.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 5);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: gmatch matched\n--- config\n    location /re {\n        content_by_lua '\n            for m in ngx.re.gmatch(\"hello, halo\", \"h[a-z]|h[a-z][a-z]\", \"d\") do\n                if m then\n                    ngx.say(m[0])\n                    ngx.say(m[1])\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhel\nnil\nhal\nnil\n\n\n\n=== TEST 2: d + j\n--- config\n    location /re {\n        content_by_lua '\n            for m in ngx.re.gmatch(\"hello, halo\", \"h[a-z]|h[a-z][a-z]\", \"dj\") do\n                if m then\n                    ngx.say(m[0])\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhel\nhal\n\n\n\n=== TEST 3: fail to match\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[0-9]\", \"d\")\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnil\nnil\nnil\n\n\n\n=== TEST 4: gmatch matched but no iterate\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[a-z]+\", \"d\")\n            ngx.say(\"done\")\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ndone\n\n\n\n=== TEST 5: gmatch matched but only iterate once and still matches remain\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[a-z]+\", \"d\")\n            local m = it()\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\n\n\n\n=== TEST 6: gmatch matched + o\n--- config\n    location /re {\n        content_by_lua '\n            for m in ngx.re.gmatch(\"hello, world\", \"[a-z]+\", \"do\") do\n                if m then\n                    ngx.say(m[0])\n                else\n                    ngx.say(\"not matched: \", m)\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\nworld\n\n\n\n=== TEST 7: fail to match + o\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[0-9]\", \"do\")\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n\n            local m = it()\n            if m then ngx.say(m[0]) else ngx.say(m) end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnil\nnil\nnil\n\n\n\n=== TEST 8: gmatch matched but no iterate + o\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[a-z]+\", \"do\")\n            ngx.say(\"done\")\n        ';\n    }\n--- request\n    GET /re\n--- response_body\ndone\n\n\n\n=== TEST 9: gmatch matched but only iterate once and still matches remain + o\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"hello, world\", \"[a-z]+\", \"do\")\n            local m = it()\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello\n\n\n\n=== TEST 10: bad pattern\n--- config\n    location /re {\n        content_by_lua '\n            local it, err = ngx.re.gmatch(\"hello\\\\nworld\", \"(abc\", \"d\")\n            if not it then\n                ngx.say(\"error: \", err)\n                return\n            end\n            ngx.say(\"success\")\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: UTF-8 mode without UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"你好\", \".\", \"Ud\")\n            local m = it()\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_dfa_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 2000\n\n--- request\n    GET /re\n--- response_body\n你\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: UTF-8 mode with UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local it = ngx.re.gmatch(\"你好\", \".\", \"ud\")\n            local m = it()\n            if m then\n                ngx.say(m[0])\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_dfa_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 0\n\n--- request\n    GET /re\n--- response_body\n你\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: gmatched with submatch captures\n--- config\n    location /re {\n        content_by_lua '\n            for m in  ngx.re.gmatch(\"hello\", \"(he|hell)\", \"d\") do\n                if m then\n                    ngx.say(m[0])\n                    ngx.say(m[1])\n                    ngx.say(m[2])\n                else\n                    ngx.say(\"not matched!\")\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhell\nnil\nnil\n\n\n\n=== TEST 14: gmatched with submatch captures (compile once)\n--- config\n    location /re {\n        content_by_lua '\n            for m in  ngx.re.gmatch(\"hello\", \"(he|hell)\", \"od\") do\n                if m then\n                    ngx.say(m[0])\n                    ngx.say(m[1])\n                    ngx.say(m[2])\n                else\n                    ngx.say(\"not matched!\")\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhell\nnil\nnil\n"
  },
  {
    "path": "t/051-sub-jit.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 6);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: matched with j\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, 1234 5678\", \"([0-9]+)\", \"world\", \"j\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world 5678: 1\n--- error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\\n\"\n:\n\"pcre JIT compiling result: 1\\n\"\n\n\n\n=== TEST 2: not matched with j\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, world\", \"[0-9]+\", \"hiya\", \"j\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world: 0\n--- error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\\n\"\n:\n\"pcre JIT compiling result: 1\\n\"\n\n\n\n=== TEST 3: matched with jo\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, 1234 5678\", \"([0-9]+)\", \"world\", \"jo\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world 5678: 1\n\n--- grep_error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\"\n:\n\"pcre JIT compiling result: 1\"\n\n--- grep_error_log_out eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n[\"pcre2 JIT compiled successfully\\n\", \"\"]\n:\n[\"pcre JIT compiling result: 1\\n\", \"\"]\n\n\n\n=== TEST 4: not matched with jo\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, world\", \"[0-9]+\", \"hiya\", \"jo\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world: 0\n\n--- grep_error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\"\n:\n\"pcre JIT compiling result: 1\"\n\n--- grep_error_log_out eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n[\"pcre2 JIT compiled successfully\\n\", \"\"]\n:\n[\"pcre JIT compiling result: 1\\n\", \"\"]\n\n\n\n=== TEST 5: bad pattern\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"hello\\\\nworld\", \"(abc\", \"world\", \"j\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: bad pattern + o\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub( \"hello\\\\nworld\", \"(abc\", \"world\", \"jo\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/052-sub-dfa.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 8);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: matched with d\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, 1234 5678\", \"[0-9]|[0-9][0-9]\", \"world\", \"d\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world34 5678: 1\n\n\n\n=== TEST 2: not matched with d\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, world\", \"[0-9]+\", \"hiya\", \"d\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world: 0\n\n\n\n=== TEST 3: matched with do\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, 1234 5678\", \"[0-9]|[0-9][0-9]\", \"world\", \"do\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world34 5678: 1\n\n\n\n=== TEST 4: not matched with do\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.sub(\"hello, world\", \"[0-9]+\", \"hiya\", \"do\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world: 0\n\n\n\n=== TEST 5: bad pattern\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"hello\\\\nworld\", \"(abc\", \"world\", \"j\")\n            if s then\n                ngx.say(s, \": \", n)\n\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: bad pattern + o\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"hello\\\\nworld\", \"(abc\", \"world\", \"jo\")\n            if s then\n                ngx.say(s, \": \", n)\n\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: UTF-8 mode without UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"你好\", \".\", \"a\", \"Ud\")\n            if s then\n                ngx.say(\"s: \", s)\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_dfa_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 2000\n\n--- request\n    GET /re\n--- response_body\ns: a好\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: UTF-8 mode with UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.sub(\"你好\", \".\", \"a\", \"ud\")\n            if s then\n                ngx.say(\"s: \", s)\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_dfa_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 0\n\n--- request\n    GET /re\n--- response_body\ns: a好\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: sub with d\n--- config\n    location /re {\n        content_by_lua '\n            ngx.say(ngx.re.sub(\"hello\", \"(he|hell)\", function (m) ngx.say(m[0]) ngx.say(m[1]) return \"x\" end, \"d\"))\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhell\nnil\nxo1\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: sub with d + o\n--- config\n    location /re {\n        content_by_lua '\n            ngx.say(ngx.re.sub(\"hello\", \"(he|hell)\", function (m) ngx.say(m[0]) ngx.say(m[1]) return \"x\" end, \"do\"))\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhell\nnil\nxo1\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/053-gsub-jit.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 6);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: matched with j\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, 1234 5678\", \"([0-9]+)\", \"world\", \"j\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world world: 2\n--- error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\\n\"\n:\n\"pcre JIT compiling result: 1\\n\"\n\n\n\n=== TEST 2: not matched with j\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, world\", \"[0-9]+\", \"hiya\", \"j\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world: 0\n--- error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\\n\"\n:\n\"pcre JIT compiling result: 1\\n\"\n\n\n\n=== TEST 3: matched with jo\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, 1234 5678\", \"([0-9]+)\", \"world\", \"jo\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world world: 2\n\n--- grep_error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\"\n:\n\"pcre JIT compiling result: 1\"\n\n--- grep_error_log_out eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n[\"pcre2 JIT compiled successfully\\n\", \"\"]\n:\n[\"pcre JIT compiling result: 1\\n\", \"\"]\n\n\n\n=== TEST 4: not matched with jo\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, world\", \"[0-9]+\", \"hiya\", \"jo\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world: 0\n\n--- grep_error_log eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"pcre2 JIT compiled successfully\"\n:\n\"pcre JIT compiling result: 1\"\n\n--- grep_error_log_out eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n[\"pcre2 JIT compiled successfully\\n\", \"\"]\n:\n[\"pcre JIT compiling result: 1\\n\", \"\"]\n\n\n\n=== TEST 5: bad pattern\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.gsub(\"hello\\\\nworld\", \"(abc\", \"world\", \"j\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: bad pattern + o\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.gsub(\"hello\\\\nworld\", \"(abc\", \"world\", \"jo\")\n            if s then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/054-gsub-dfa.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 7);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: matched with d\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, 1234 5678\", \"[0-9]|[0-9][0-9]\", \"world\", \"d\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, worldworld worldworld: 4\n\n\n\n=== TEST 2: not matched with d\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, world\", \"[0-9]+\", \"hiya\", \"d\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world: 0\n\n\n\n=== TEST 3: matched with do\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, 1234 5678\", \"[0-9]|[0-9][0-9]\", \"world\", \"do\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, worldworld worldworld: 4\n\n\n\n=== TEST 4: not matched with do\n--- config\n    location /re {\n        content_by_lua '\n            local s, n = ngx.re.gsub(\"hello, world\", \"[0-9]+\", \"hiya\", \"do\")\n            if n then\n                ngx.say(s, \": \", n)\n            else\n                ngx.say(s)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhello, world: 0\n\n\n\n=== TEST 5: bad pattern\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.gsub(\"hello\\\\nworld\", \"(abc\", \"world\", \"j\")\n            if s then\n                ngx.say(\"gsub: \", n)\n\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n\n\n\n=== TEST 6: bad pattern + o\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.gsub(\"hello\\\\nworld\", \"(abc\", \"world\", \"jo\")\n            if s then\n                ngx.say(\"gsub: \", n)\n            else\n                ngx.say(\"error: \", err)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: UTF-8 mode without UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.gsub(\"你好\", \".\", \"a\", \"Ud\")\n            if s then\n                ngx.say(\"s: \", s)\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_dfa_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 2000\nexec opts: 2000\nexec opts: 2000\n\n--- request\n    GET /re\n--- response_body\ns: aa\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: UTF-8 mode with UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local s, n, err = ngx.re.gsub(\"你好\", \".\", \"a\", \"ud\")\n            if s then\n                ngx.say(\"s: \", s)\n            end\n        ';\n    }\n--- stap\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_dfa_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 0\nexec opts: 0\nexec opts: 0\n\n--- request\n    GET /re\n--- response_body\ns: aa\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: gsub with d\n--- config\n    location /re {\n        content_by_lua '\n            ngx.say(ngx.re.gsub(\"hello\", \"(he|hell)\", function (m) ngx.say(m[0]) ngx.say(m[1]) return \"x\" end, \"d\"))\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhell\nnil\nxo1\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: gsub with d + o\n--- config\n    location /re {\n        content_by_lua '\n            ngx.say(ngx.re.gsub(\"hello\", \"(he|hell)\", function (m) ngx.say(m[0]) ngx.say(m[1]) return \"x\" end, \"do\"))\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nhell\nnil\nxo1\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/055-subreq-vars.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 5);\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: set non-existent variables via \"vars\" option\n--- config\n    location /other {\n        content_by_lua '\n            ngx.say(\"dog = \", ngx.var.dog)\n            ngx.say(\"cat = \", ngx.var.cat)\n        ';\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { vars = { dog = \"hello\", cat = 32 }});\n\n            ngx.print(res.body)\n        ';\n    }\n\n--- stap2\n\nglobal delta = \"  \"\n\nF(ngx_http_finalize_request) {\n    uri = ngx_http_req_uri($r)\n    printf(\"finalize req %s: %d\\n\", uri, $rc)\n    if ($rc == 500) {\n        print_ubacktrace()\n    }\n}\n\nF(ngx_http_lua_run_thread) {\n    uri = ngx_http_req_uri($r)\n    printf(\"lua run thread %s\\n\", uri)\n}\n\nM(http-subrequest-start) {\n    r = $arg1\n    n = ngx_http_subreq_depth(r)\n    pr = ngx_http_req_parent(r)\n    printf(\"%sbegin %s -> %s (%d)\\n\", ngx_indent(n, delta),\n        ngx_http_req_uri(pr),\n        ngx_http_req_uri(r),\n        n)\n}\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_log eval\nqr/variable \"(dog|cat)\" cannot be assigned a value \\(maybe you forgot to define it first\\?\\)/\n--- error_code: 500\n\n\n\n=== TEST 2: set non-existent variables via \"vars\" option\n--- config\n    location /other {\n        content_by_lua '\n            ngx.say(\"dog = \", ngx.var.dog)\n            ngx.say(\"cat = \", ngx.var.cat)\n        ';\n    }\n\n    location /lua {\n        set $dog '';\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { vars = { dog = \"hello\", cat = 32 }});\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_log chop\nvariable \"cat\" cannot be assigned a value (maybe you forgot to define it first?)\n--- error_code: 500\n\n\n\n=== TEST 3: good \"vars\" option: user variables\n--- config\n    location /other {\n        content_by_lua '\n            ngx.say(\"dog = \", ngx.var.dog)\n            ngx.say(\"cat = \", ngx.var.cat)\n        ';\n    }\n\n    location /lua {\n        set $dog '';\n        set $cat '';\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { vars = { dog = \"hello\", cat = 32 }});\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ndog = hello\ncat = 32\n\n\n\n=== TEST 4: bad \"vars\" option value\n--- config\n    location /other {\n        content_by_lua '\n            ngx.say(\"dog = \", ngx.var.dog)\n            ngx.say(\"cat = \", ngx.var.cat)\n        ';\n    }\n\n    location /lua {\n        set $dog '';\n        set $cat '';\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { vars = \"hello\" });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log chop\nBad vars option value\n\n\n\n=== TEST 5: bad \"vars\" option value value\n--- config\n    location /other {\n        content_by_lua '\n            ngx.say(\"dog = \", ngx.var.dog)\n            ngx.say(\"cat = \", ngx.var.cat)\n        ';\n    }\n\n    location /lua {\n        set $dog '';\n        set $cat '';\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { vars = { cat = true } });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log chop\nattempt to use bad variable value type boolean\n\n\n\n=== TEST 6: good \"vars\" option: builtin variables\n--- config\n    location /other {\n        echo \"args: $args\";\n    }\n\n    location /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { vars = { args = \"a=hello&b=32\" }});\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nargs: a=hello&b=32\n\n\n\n=== TEST 7: setting non-changeable vars\n--- config\n    location /other {\n        echo \"query string: $query_string\";\n    }\n\n    location /lua {\n        content_by_lua '\n            res = ngx.location.capture(\"/other\",\n                { vars = { query_string = \"hello\" } });\n\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log chop\nvariable \"query_string\" not changeable\n\n\n\n=== TEST 8: copy all vars\n--- config\n    location /other {\n        set $dog \"$dog world\";\n        echo \"$uri dog: $dog\";\n    }\n\n    location /lua {\n        set $dog 'hello';\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { copy_all_vars = true });\n\n            ngx.print(res.body)\n            ngx.say(ngx.var.uri, \": \", ngx.var.dog)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n/other dog: hello world\n/lua: hello\n\n\n\n=== TEST 9: share all vars\n--- config\n    location /other {\n        set $dog \"$dog world\";\n        echo \"$uri dog: $dog\";\n    }\n\n    location /lua {\n        set $dog 'hello';\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { share_all_vars = true });\n\n            ngx.print(res.body)\n            ngx.say(ngx.var.uri, \": \", ngx.var.dog)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n/other dog: hello world\n/lua: hello world\n\n\n\n=== TEST 10: vars takes priority over copy_all_vars\n--- config\n    location /other {\n        set $dog \"$dog world\";\n        echo \"$uri dog: $dog\";\n    }\n\n    location /lua {\n        set $dog 'hello';\n        content_by_lua '\n            local res = ngx.location.capture(\"/other\",\n                { vars = { dog = \"hiya\" }, copy_all_vars = true });\n\n            ngx.print(res.body)\n            ngx.say(ngx.var.uri, \": \", ngx.var.dog)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n/other dog: hiya world\n/lua: hello\n\n\n\n=== TEST 11: capture_multi: good \"vars\" option: user variables\n--- config\n    location /other {\n        content_by_lua '\n            ngx.say(\"dog = \", ngx.var.dog)\n            ngx.say(\"cat = \", ngx.var.cat)\n        ';\n    }\n\n    location /lua {\n        set $dog 'blah';\n        set $cat 'foo';\n        content_by_lua '\n            local res1, res2 = ngx.location.capture_multi{\n                {\"/other/1\",\n                    { vars = { dog = \"hello\", cat = 32 }}\n                },\n                {\"/other/2\",\n                    { vars = { dog = \"hiya\", cat = 56 }}\n                }\n            };\n\n            ngx.print(res1.body)\n            ngx.print(res2.body)\n            ngx.say(\"parent dog: \", ngx.var.dog)\n            ngx.say(\"parent cat: \", ngx.var.cat)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ndog = hello\ncat = 32\ndog = hiya\ncat = 56\nparent dog: blah\nparent cat: foo\n"
  },
  {
    "path": "t/056-flush.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nBEGIN {\n    $ENV{TEST_NGINX_POSTPONE_OUTPUT} = 1;\n}\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 60;\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: flush wait - content\n--- config\n    location /test {\n        content_by_lua '\n            ngx.say(\"hello, world\")\n            local ok, err = ngx.flush(true)\n            if not ok then\n                ngx.log(ngx.ERR, \"flush failed: \", err)\n                return\n            end\n            ngx.say(\"hiya\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nhello, world\nhiya\n--- no_error_log\n[error]\n--- error_log\nlua reuse free buf chain, but reallocate memory because 5 >= 0\n--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n\n\n\n=== TEST 2: flush no wait - content\n--- config\n    send_timeout 500ms;\n    location /test {\n        content_by_lua '\n            ngx.say(\"hello, world\")\n            local ok, err = ngx.flush(false)\n            if not ok then\n                ngx.log(ngx.ERR, \"flush failed: \", err)\n                return\n            end\n            ngx.say(\"hiya\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nhello, world\nhiya\n\n\n\n=== TEST 3: flush wait - rewrite\n--- config\n    location /test {\n        rewrite_by_lua '\n            ngx.say(\"hello, world\")\n            ngx.flush(true)\n            ngx.say(\"hiya\")\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /test\n--- response_body\nhello, world\nhiya\n\n\n\n=== TEST 4: flush no wait - rewrite\n--- config\n    location /test {\n        rewrite_by_lua '\n            ngx.say(\"hello, world\")\n            ngx.flush(false)\n            ngx.say(\"hiya\")\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /test\n--- response_body\nhello, world\nhiya\n\n\n\n=== TEST 5: http 1.0 (sync)\n--- config\n    location /test {\n        content_by_lua '\n            ngx.say(\"hello, world\")\n            ngx.flush(true)\n            ngx.say(\"hiya\")\n            ngx.flush(true)\n            ngx.say(\"blah\")\n        ';\n    }\n--- request\nGET /test HTTP/1.0\n--- response_body\nhello, world\nhiya\nblah\n--- response_headers\nContent-Length: 23\n--- timeout: 5\n--- error_log\nlua buffering output bufs for the HTTP 1.0 request\nlua http 1.0 buffering makes ngx.flush() a no-op\n\n\n\n=== TEST 6: http 1.0 (async)\n--- config\n    location /test {\n        content_by_lua '\n            ngx.say(\"hello, world\")\n            local ok, err = ngx.flush(false)\n            if not ok then\n                ngx.log(ngx.WARN, \"1: failed to flush: \", err)\n            end\n            ngx.say(\"hiya\")\n            local ok, err = ngx.flush(false)\n            if not ok then\n                ngx.log(ngx.WARN, \"2: failed to flush: \", err)\n            end\n            ngx.say(\"blah\")\n        ';\n    }\n--- request\nGET /test HTTP/1.0\n--- response_body\nhello, world\nhiya\nblah\n--- response_headers\nContent-Length: 23\n--- error_log\nlua buffering output bufs for the HTTP 1.0 request\nlua http 1.0 buffering makes ngx.flush() a no-op\n1: failed to flush: buffering\n2: failed to flush: buffering\n--- timeout: 5\n\n\n\n=== TEST 7: flush wait - big data\n--- config\n    location /test {\n        content_by_lua '\n            ngx.say(string.rep(\"a\", 1024 * 64))\n            ngx.flush(true)\n            ngx.say(\"hiya\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nhello, world\nhiya\n--- SKIP\n\n\n\n=== TEST 8: flush wait - content\n--- config\n    location /test {\n        content_by_lua '\n            ngx.say(\"hello, world\")\n            ngx.flush(true)\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n            ngx.flush(true)\n        ';\n    }\n    location /sub {\n        echo sub;\n    }\n--- request\nGET /test\n--- response_body\nhello, world\nsub\n\n\n\n=== TEST 9: http 1.0 (sync + buffering off)\n--- config\n    lua_http10_buffering off;\n    location /test {\n        content_by_lua '\n            ngx.say(\"hello, world\")\n            ngx.flush(true)\n            ngx.say(\"hiya\")\n            ngx.flush(true)\n            ngx.say(\"blah\")\n        ';\n    }\n--- request\nGET /test HTTP/1.0\n--- response_body\nhello, world\nhiya\nblah\n--- response_headers\n!Content-Length\n--- timeout: 5\n--- no_error_log\nlua buffering output bufs for the HTTP 1.0 request\nlua http 1.0 buffering makes ngx.flush() a no-op\n\n\n\n=== TEST 10: http 1.0 (async)\n--- config\n    lua_http10_buffering on;\n    location /test {\n        lua_http10_buffering off;\n        content_by_lua '\n            ngx.say(\"hello, world\")\n            ngx.flush(false)\n            ngx.say(\"hiya\")\n            ngx.flush(false)\n            ngx.say(\"blah\")\n        ';\n    }\n--- request\nGET /test HTTP/1.0\n--- response_body\nhello, world\nhiya\nblah\n--- response_headers\n!Content-Length\n--- no_error_log\nlua buffering output bufs for the HTTP 1.0 request\nlua http 1.0 buffering makes ngx.flush() a no-op\n--- timeout: 5\n\n\n\n=== TEST 11: http 1.0 (sync) - buffering explicitly off\n--- config\n    location /test {\n        lua_http10_buffering on;\n        content_by_lua '\n            ngx.say(\"hello, world\")\n            ngx.flush(true)\n            ngx.say(\"hiya\")\n            ngx.flush(true)\n            ngx.say(\"blah\")\n        ';\n    }\n--- request\nGET /test HTTP/1.0\n--- response_body\nhello, world\nhiya\nblah\n--- response_headers\nContent-Length: 23\n--- timeout: 5\n--- error_log\nlua buffering output bufs for the HTTP 1.0 request\nlua http 1.0 buffering makes ngx.flush() a no-op\n\n\n\n=== TEST 12: http 1.0 (async) - buffering explicitly off\n--- config\n    location /test {\n        lua_http10_buffering on;\n        content_by_lua '\n            ngx.say(\"hello, world\")\n            ngx.flush(false)\n            ngx.say(\"hiya\")\n            ngx.flush(false)\n            ngx.say(\"blah\")\n        ';\n    }\n--- request\nGET /test HTTP/1.0\n--- response_body\nhello, world\nhiya\nblah\n--- response_headers\nContent-Length: 23\n--- error_log\nlua buffering output bufs for the HTTP 1.0 request\nlua http 1.0 buffering makes ngx.flush() a no-op\n--- timeout: 5\n\n\n\n=== TEST 13: flush wait in a user coroutine\n--- config\n    location /test {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello, world\")\n                ngx.flush(true)\n                coroutine.yield()\n                ngx.say(\"hiya\")\n            end\n            local c = coroutine.create(f)\n            ngx.say(coroutine.resume(c))\n            ngx.say(coroutine.resume(c))\n        ';\n    }\n--- request\nGET /test\n--- stap2\nF(ngx_http_lua_wev_handler) {\n    printf(\"wev handler: wev:%d\\n\", $r->connection->write->ready)\n}\n\nglobal ids, cur\n\nfunction gen_id(k) {\n    if (ids[k]) return ids[k]\n    ids[k] = ++cur\n    return cur\n}\n\nF(ngx_http_handler) {\n    delete ids\n    cur = 0\n}\n\n/*\nF(ngx_http_lua_run_thread) {\n    id = gen_id($ctx->cur_co)\n    printf(\"run thread %d\\n\", id)\n}\n\nprobe process(\"/usr/local/openresty-debug/luajit/lib/libluajit-5.1.so.2\").function(\"lua_resume\") {\n    id = gen_id($L)\n    printf(\"lua resume %d\\n\", id)\n}\n*/\n\nM(http-lua-user-coroutine-resume) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"resume %x in %x\\n\", c, p)\n}\n\nM(http-lua-entry-coroutine-yield) {\n    println(\"entry coroutine yield\")\n}\n\n/*\nF(ngx_http_lua_coroutine_yield) {\n    printf(\"yield %x\\n\", gen_id($L))\n}\n*/\n\nM(http-lua-user-coroutine-yield) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"yield %x in %x\\n\", c, p)\n}\n\nF(ngx_http_lua_atpanic) {\n    printf(\"lua atpanic(%d):\", gen_id($L))\n    print_ubacktrace();\n}\n\nM(http-lua-user-coroutine-create) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"create %x in %x\\n\", c, p)\n}\n\nF(ngx_http_lua_ngx_exec) { println(\"exec\") }\n\nF(ngx_http_lua_ngx_exit) { println(\"exit\") }\n\nF(ngx_http_writer) { println(\"http writer\") }\n\n--- response_body\nhello, world\ntrue\nhiya\ntrue\n--- error_log\nlua reuse free buf memory 13 >= 5\n\n\n\n=== TEST 14: flush before sending out the header\n--- config\n    location /test {\n        content_by_lua '\n            ngx.flush()\n            ngx.status = 404\n            ngx.say(\"not found\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nnot found\n--- error_code: 404\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: flush wait - gzip\n--- config\n    gzip             on;\n    gzip_min_length  1;\n    gzip_types       text/plain;\n\n    location /test {\n        content_by_lua '\n            ngx.say(\"hello, world\")\n            local ok, err = ngx.flush(true)\n            if not ok then\n                ngx.log(ngx.ERR, \"flush failed: \", err)\n                return\n            end\n            ngx.say(\"hiya\")\n        ';\n    }\n--- request\nGET /test\n--- more_headers\nAccept-Encoding: gzip\n--- response_body_like: .{15}\n--- response_headers\nContent-Encoding: gzip\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: flush wait - gunzip\n--- config\n    location /test {\n        gunzip on;\n        content_by_lua '\n            local f, err = io.open(ngx.var.document_root .. \"/gzip.bin\", \"r\")\n            if not f then\n                ngx.say(\"failed to open file: \", err)\n                return\n            end\n            local data = f:read(100)\n            ngx.header.content_encoding = \"gzip\"\n            ngx.print(data)\n            local ok, err = ngx.flush(true)\n            if not ok then\n                ngx.log(ngx.ERR, \"flush failed: \", err)\n                return\n            end\n            data = f:read(\"*a\")\n            ngx.print(data)\n        ';\n    }\n--- user_files eval\n\">>> gzip.bin\n\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x62\\x64\\x62\\x62\\x61\\x62\\x64\\x63\\x61\\xe4\\xe0\\xe2\\xe6\\xe4\\x61\\xe4\\xe4\\xe7\\x63\\x12\\xe4\\xe1\\xe0\\x60\\x14\\x12\\xe3\\x91\\xe4\\xe4\\xe4\\x13\\x60\\xe3\\x95\\x12\\x90\\x15\\xe0\\x11\\x50\\x92\\xd1\\x16\\x17\\xe2\\xd3\\x17\\x14\\x11\\x95\\x95\\x57\\x96\\x63\\x37\\xd2\\x36\\xd6\\x51\\x34\\xb1\\xe6\\x62\\x17\\x95\\xb0\\x77\\x60\\xe3\\x96\\x33\\x95\\xb6\\x91\\x75\\x97\\x30\\xe4\\x66\\x0c\\xd0\\xe3\\xe0\\xb5\\xd3\\x33\\xf6\\x90\\x16\\xb2\\x90\\x77\\x56\\x31\\xe7\\x55\\x32\\x11\\x74\\xe0\\x02\\x00\\x00\\x00\\xff\\xff\\xcb\\xc8\\xac\\x4c\\xe4\\x02\\x00\\x19\\x15\\xa9\\x77\\x6a\\x00\\x00\\x00\"\n--- request\nGET /test\n--- ignore_response\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: limit_rate\n--- quic_max_idle_timeout: 2\n--- config\n    location /test {\n        limit_rate 150;\n        content_by_lua '\n            local begin = ngx.now()\n            for i = 1, 2 do\n                ngx.print(string.rep(\"a\", 100))\n                local ok, err = ngx.flush(true)\n                if not ok then\n                    ngx.log(ngx.ERR, \"failed to flush: \", err)\n                end\n            end\n            local elapsed = ngx.now() - begin\n            ngx.log(ngx.WARN, \"lua writes elapsed \", elapsed, \" sec\")\n        ';\n    }\n--- request\nGET /test\n--- response_body eval\n\"a\" x 200\n--- error_log eval\nmy @errlog;\nif (defined $ENV{TEST_NGINX_USE_HTTP2}) {\n    @errlog = [\nqr/lua writes elapsed (?:0\\.[7-9]\\d+|[12]\\.\\d+) sec/,\nqr/lua flush requires waiting: buffered 0x[0-9a-f]+, delayed:1/,\n];\n} else {\n    @errlog = [\nqr/lua writes elapsed [12](?:\\.\\d+)? sec/,\nqr/lua flush requires waiting: buffered 0x[0-9a-f]+, delayed:1/,\n];\n}\n@errlog;\n--- no_error_log\n[error]\n--- timeout: 4\n"
  },
  {
    "path": "t/057-flush-timeout.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nBEGIN {\n    if (!defined $ENV{LD_PRELOAD}) {\n        $ENV{LD_PRELOAD} = '';\n    }\n\n    if ($ENV{LD_PRELOAD} !~ /\\bmockeagain\\.so\\b/) {\n        $ENV{LD_PRELOAD} = \"mockeagain.so $ENV{LD_PRELOAD}\";\n    }\n\n    if ($ENV{MOCKEAGAIN} eq 'r') {\n        $ENV{MOCKEAGAIN} = 'rw';\n\n    } else {\n        $ENV{MOCKEAGAIN} = 'w';\n    }\n\n    $ENV{TEST_NGINX_EVENT_TYPE} = 'poll';\n    $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'hello, world';\n    $ENV{TEST_NGINX_POSTPONE_OUTPUT} = 1;\n    delete($ENV{TEST_NGINX_USE_HTTP2});\n\n    if ($ENV{TEST_NGINX_USE_HTTP3}) {\n        $SkipReason = \"HTTP3 does not support mockeagain\";\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 17;\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: flush wait - timeout\n--- config\n    send_timeout 100ms;\n    location /test {\n        content_by_lua '\n            ngx.say(\"hello, world\")\n            ngx.flush(true)\n            ngx.say(\"hiya\")\n        ';\n    }\n--- request\nGET /test\n--- ignore_response\n--- error_log eval\nqr/client timed out \\(\\d+: .*?timed out\\)/\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: send timeout timer got removed in time\n--- config\n    send_timeout 1234ms;\n    location /test {\n        content_by_lua '\n            ngx.say(string.rep(\"blah blah blah\", 10))\n            -- ngx.flush(true)\n            ngx.eof()\n            for i = 1, 20 do\n                ngx.sleep(0.1)\n            end\n        ';\n    }\n--- request\nGET /test\n--- stap\nglobal evtime\n\nF(ngx_http_handler) {\n    delete evtime\n}\n\nM(timer-add) {\n    if ($arg2 == 1234) {\n        printf(\"add timer %d\\n\", $arg2)\n        evtime[$arg1] = $arg2\n    }\n}\n\nM(timer-del) {\n    time = evtime[$arg1]\n    if (time == 1234) {\n        printf(\"del timer %d\\n\", time)\n    }\n}\n\nM(timer-expire) {\n    time = evtime[$arg1]\n    if (time == 1234) {\n        printf(\"expire timer %d\\n\", time)\n        #print_ubacktrace()\n    }\n}\n/*\nprobe syscall.writev.return {\n    if (pid() == target()) {\n        printf(\"writev: %s\\n\", retstr)\n    }\n}\n*/\n--- stap_out\nadd timer 1234\ndel timer 1234\n--- ignore_response\n--- no_error_log\n[error]\n--- timeout: 3\n\n\n\n=== TEST 3: exit in user thread (entry thread is still pending on ngx.flush)\n--- config\n    send_timeout 200ms;\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n\n            ngx.say(\"hello, world!\")\n            ngx.flush(true)\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n\n/*\nF(ngx_http_finalize_request) {\n    printf(\"finalize request: c:%d, a:%d, cb:%d, rb:%d\\n\", $r->main->count,\n        $r == $r->connection->data, $r->connection->buffered, $r->buffered)\n}\n\nF(ngx_http_set_write_handler) {\n    println(\"set write handler\")\n}\n*/\n\nF(ngx_http_lua_flush_cleanup) {\n    println(\"lua flush cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua flush cleanup\ndelete timer 200\ndelete thread 1\nadd timer 200\nexpire timer 200\nfree request\n\n--- ignore_response\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: flush wait - return \"timeout\" error\n--- config\n    send_timeout 100ms;\n    location /test {\n        content_by_lua '\n            ngx.say(\"hello, world\")\n            local ok, err = ngx.flush(true)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to flush: \", err)\n                return\n            end\n            ngx.say(\"hiya\")\n        ';\n    }\n--- request\nGET /test\n--- ignore_response\n--- error_log eval\n[\nqr/client timed out \\(\\d+: .*?timed out\\)/,\n'failed to flush: timeout',\n]\n--- no_error_log\n[alert]\n\n\n\n=== TEST 5: flush wait in multiple user threads - return \"timeout\" error\n--- config\n    send_timeout 100ms;\n    location /test {\n        content_by_lua '\n            ngx.say(\"hello, world\")\n\n            local function run(tag)\n                local ok, err = ngx.flush(true)\n                if not ok then\n                    ngx.log(ngx.ERR, \"thread \", tag, \": failed to flush: \", err)\n                    return\n                end\n                ngx.say(\"hiya\")\n            end\n\n            local function new_thread(tag)\n                local ok, err = ngx.thread.spawn(run, tag)\n                if not ok then\n                    return error(\"failed to spawn thread: \", err)\n                end\n            end\n\n            new_thread(\"A\")\n            new_thread(\"B\")\n            run(\"main\")\n        ';\n    }\n--- request\nGET /test\n--- ignore_response\n--- error_log eval\n[\nqr/client timed out \\(\\d+: .*?timed out\\)/,\n'thread main: failed to flush: timeout',\n'thread A: failed to flush: timeout',\n'thread B: failed to flush: timeout',\n]\n--- no_error_log\n[alert]\n\n\n\n=== TEST 6: flush wait - client abort connection prematurely\n--- config\n    #send_timeout 100ms;\n    location /test {\n        limit_rate 2;\n        content_by_lua '\n            ngx.say(\"hello, lua\")\n            local ok, err = ngx.flush(true)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to flush: \", err)\n                return\n            end\n            ngx.say(\"hiya\")\n        ';\n    }\n--- request\nGET /test\n--- ignore_response\n--- error_log eval\n[\nqr/writev\\(\\) failed .*? Broken pipe/i,\nqr/failed to flush: client aborted/,\n]\n--- no_error_log\n[alert]\n\n--- timeout: 0.2\n--- abort\n--- wait: 1\n"
  },
  {
    "path": "t/058-tcp-socket.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse Test::Nginx::Socket::Lua::Stream;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 21);\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: no trailing newline\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 1234;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            sock:close()\n            ngx.say(\"closed\")\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.print(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 3\nreceived: Connection: close\nreceived: \nfailed to receive a line: closed [foo]\nclosed\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: no resolver defined\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 1234;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"agentzh.org\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nfailed to connect: no resolver defined to resolve \"agentzh.org\"\nconnected: nil\nfailed to send request: closed\n--- error_log\nattempt to send data on a closed socket:\n\n\n\n=== TEST 4: with resolver\n--- timeout: 10\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = 80\n            local ok, err = sock:connect(\"agentzh.org\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET / HTTP/1.0\\\\r\\\\nHost: agentzh.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local line, err = sock:receive()\n            if line then\n                ngx.say(\"first line received: \", line)\n\n            else\n                ngx.say(\"failed to receive the first line: \", err)\n            end\n\n            line, err = sock:receive()\n            if line then\n                ngx.say(\"second line received: \", line)\n\n            else\n                ngx.say(\"failed to receive the second line: \", err)\n            end\n        ';\n    }\n\n--- request\nGET /t\n--- response_body_like\nconnected: 1\nrequest sent: 56\nfirst line received: HTTP\\/1\\.1 200 OK\nsecond line received: (?:Date|Server): .*?\n--- no_error_log\n[error]\n--- timeout: 10\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 5: connection refused (tcp)\n--- no_http2\n--- config\n    location /test {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", 16787)\n            ngx.say(\"connect: \", ok, \" \", err)\n\n            local bytes\n            bytes, err = sock:send(\"hello\")\n            ngx.say(\"send: \", bytes, \" \", err)\n\n            local line\n            line, err = sock:receive()\n            ngx.say(\"receive: \", line, \" \", err)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n--- request\n    GET /test\n--- response_body\nconnect: nil connection refused\nsend: nil closed\nreceive: nil closed\nclose: nil closed\n--- error_log eval\nqr/connect\\(\\) failed \\(\\d+: Connection refused\\)/\n\n\n\n=== TEST 6: connection timeout (tcp)\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_socket_connect_timeout 100ms;\n    lua_socket_send_timeout 100ms;\n    lua_socket_read_timeout 100ms;\n    resolver_timeout 3s;\n    location /test {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            ngx.say(\"connect: \", ok, \" \", err)\n\n            local bytes\n            bytes, err = sock:send(\"hello\")\n            ngx.say(\"send: \", bytes, \" \", err)\n\n            local line\n            line, err = sock:receive()\n            ngx.say(\"receive: \", line, \" \", err)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n--- request\n    GET /test\n--- response_body\nconnect: nil timeout\nsend: nil closed\nreceive: nil closed\nclose: nil closed\n--- error_log\nlua tcp socket connect timed out, upstream: 127.0.0.2:12345(127.0.0.2)\n--- timeout: 10\n\n\n\n=== TEST 7: not closed manually\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: resolver error (host not found)\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = 80\n            local ok, err = sock:connect(\"blah-blah-not-found.agentzh.org\", port)\n            print(\"connected: \", ok, \" \", err, \" \", not ok)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET / HTTP/1.0\\\\r\\\\nHost: agentzh.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n        ';\n    }\n--- request\nGET /t\n--- response_body_like\n^failed to connect: blah-blah-not-found\\.agentzh\\.org could not be resolved(?: \\(3: Host not found\\))?\nconnected: nil\nfailed to send request: closed$\n--- error_log\nattempt to send data on a closed socket\n--- timeout: 10\n\n\n\n=== TEST 9: resolver error (timeout)\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 1ms;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = 80\n            local ok, err = sock:connect(\"blah-blah-not-found.agentzh.org\", port)\n            print(\"connected: \", ok, \" \", err, \" \", not ok)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET / HTTP/1.0\\\\r\\\\nHost: agentzh.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n        ';\n    }\n--- request\nGET /t\n--- response_body_like\n^failed to connect: blah-blah-not-found\\.agentzh\\.org could not be resolved(?: \\(\\d+: (?:Operation timed out|Host not found)\\))?\nconnected: nil\nfailed to send request: closed$\n--- error_log\nattempt to send data on a closed socket\n\n\n\n=== TEST 10: explicit *l pattern for receive\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err = sock:receive(\"*l\")\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err)\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: *a pattern for receive\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local data, err = sock:receive(\"*a\")\n            if data then\n                ngx.say(\"receive: \", data)\n                ngx.say(\"err: \", err)\n\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\n\"connected: 1\nrequest sent: 57\nreceive: HTTP/1.1 200 OK\\r\nServer: nginx\\r\nContent-Type: text/plain\\r\nContent-Length: 4\\r\nConnection: close\\r\n\\r\nfoo\n\nerr: nil\nclose: 1 nil\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: mixing *a and *l patterns for receive\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local line, err = sock:receive(\"*l\")\n            if line then\n                ngx.say(\"receive: \", line)\n                ngx.say(\"err: \", err)\n\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n\n            local data\n            data, err = sock:receive(\"*a\")\n            if data then\n                ngx.say(\"receive: \", data)\n                ngx.say(\"err: \", err)\n\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\n\"connected: 1\nrequest sent: 57\nreceive: HTTP/1.1 200 OK\nerr: nil\nreceive: Server: nginx\\r\nContent-Type: text/plain\\r\nContent-Length: 4\\r\nConnection: close\\r\n\\r\nfoo\n\nerr: nil\nclose: 1 nil\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: receive by chunks\n--- no_http2\n--- timeout: 5\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local data, err, partial = sock:receive(10)\n                if data then\n                    local len = string.len(data)\n                    if len == 10 then\n                        ngx.print(\"[\", data, \"]\")\n                    else\n                        ngx.say(\"ERROR: returned invalid length of data: \", len)\n                    end\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", partial, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\n\"connected: 1\nrequest sent: 57\n[HTTP/1.1 2][00 OK\\r\nSer][ver: nginx][\\r\nContent-][Type: text][/plain\\r\nCo][ntent-Leng][th: 4\\r\nCon][nection: c][lose\\r\n\\r\nfo]failed to receive a line: closed [o\n]\nclose: 1 nil\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: receive by chunks (very small buffer)\n--- no_http2\n--- timeout: 5\n--- config\n    server_tokens off;\n    lua_socket_buffer_size 1;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local data, err, partial = sock:receive(10)\n                if data then\n                    local len = string.len(data)\n                    if len == 10 then\n                        ngx.print(\"[\", data, \"]\")\n                    else\n                        ngx.say(\"ERROR: returned invalid length of data: \", len)\n                    end\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", partial, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\n\"connected: 1\nrequest sent: 57\n[HTTP/1.1 2][00 OK\\r\nSer][ver: nginx][\\r\nContent-][Type: text][/plain\\r\nCo][ntent-Leng][th: 4\\r\nCon][nection: c][lose\\r\n\\r\nfo]failed to receive a line: closed [o\n]\nclose: 1 nil\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: line reading (very small buffer)\n--- no_http2\n--- config\n    server_tokens off;\n    lua_socket_buffer_size 1;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: ngx.socket.connect (working)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local port = ngx.var.port\n            local sock, err = ngx.socket.connect(\"127.0.0.1\", port)\n            if not sock then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected.\")\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            local ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected.\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: ngx.socket.connect() shortcut (connection refused)\n--- config\n    location /test {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local sock, err = sock:connect(\"127.0.0.1\", 16787)\n            if not sock then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes\n            bytes, err = sock:send(\"hello\")\n            ngx.say(\"send: \", bytes, \" \", err)\n\n            local line\n            line, err = sock:receive()\n            ngx.say(\"receive: \", line, \" \", err)\n\n            local ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n--- request\n    GET /test\n\n--- stap2\nM(http-lua-info) {\n    printf(\"tcp resume: %p\\n\", $coctx)\n    print_ubacktrace()\n}\n\n--- response_body\nfailed to connect: connection refused\n--- error_log eval\nqr/connect\\(\\) failed \\(\\d+: Connection refused\\)/\n\n\n\n=== TEST 18: receive by chunks (stringified size)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local data, err, partial = sock:receive(\"10\")\n                if data then\n                    local len = string.len(data)\n                    if len == 10 then\n                        ngx.print(\"[\", data, \"]\")\n                    else\n                        ngx.say(\"ERROR: returned invalid length of data: \", len)\n                    end\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", partial, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\n\"connected: 1\nrequest sent: 57\n[HTTP/1.1 2][00 OK\\r\nSer][ver: nginx][\\r\nContent-][Type: text][/plain\\r\nCo][ntent-Leng][th: 4\\r\nCon][nection: c][lose\\r\n\\r\nfo]failed to receive a line: closed [o\n]\nclose: 1 nil\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: cannot survive across request boundary (send)\n--- no_http2\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            test.go(ngx.var.port)\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction go(port)\n    if not sock then\n        sock = ngx.socket.tcp()\n        local port = ngx.var.port\n        local ok, err = sock:connect(\"127.0.0.1\", port)\n        if not ok then\n            ngx.say(\"failed to connect: \", err)\n            return\n        end\n\n        ngx.say(\"connected: \", ok)\n    end\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\nend\n--- request\nGET /t\n--- response_body_like eval\n\"^(?:connected: 1\nrequest sent: 11\nreceived: OK|failed to send request: closed)\\$\"\n\n\n\n=== TEST 20: cannot survive across request boundary (receive)\n--- no_http2\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            test.go(ngx.var.port)\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction go(port)\n    if not sock then\n        sock = ngx.socket.tcp()\n        local port = ngx.var.port\n        local ok, err = sock:connect(\"127.0.0.1\", port)\n        if not ok then\n            ngx.say(\"failed to connect: \", err)\n            return\n        end\n\n        ngx.say(\"connected: \", ok)\n\n    else\n        local line, err, part = sock:receive()\n        if line then\n            ngx.say(\"received: \", line)\n\n        else\n            ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n        end\n        return\n    end\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\nend\n\n--- stap2\nM(http-lua-info) {\n    printf(\"tcp resume\\n\")\n    print_ubacktrace()\n}\n--- request\nGET /t\n--- response_body_like eval\nqr/^(?:connected: 1\nrequest sent: 11\nreceived: OK|failed to receive a line: closed \\[nil\\])$/\n\n\n\n=== TEST 21: cannot survive across request boundary (close)\n--- no_http2\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            test.go(ngx.var.port)\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction go(port)\n    if not sock then\n        sock = ngx.socket.tcp()\n        local port = ngx.var.port\n        local ok, err = sock:connect(\"127.0.0.1\", port)\n        if not ok then\n            ngx.say(\"failed to connect: \", err)\n            return\n        end\n\n        ngx.say(\"connected: \", ok)\n\n    else\n        local ok, err = sock:close()\n        if ok then\n            ngx.say(\"successfully closed\")\n\n        else\n            ngx.say(\"failed to close: \", err)\n        end\n        return\n    end\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\nend\n--- request\nGET /t\n--- response_body_like eval\nqr/^(?:connected: 1\nrequest sent: 11\nreceived: OK|failed to close: closed)$/\n\n\n\n=== TEST 22: cannot survive across request boundary (connect)\n--- no_http2\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            test.go(ngx.var.port)\n            test.go(ngx.var.port)\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction go(port)\n    if not sock then\n        sock = ngx.socket.tcp()\n        local port = ngx.var.port\n        local ok, err = sock:connect(\"127.0.0.1\", port)\n        if not ok then\n            ngx.say(\"failed to connect: \", err)\n            return\n        end\n\n        ngx.say(\"connected: \", ok)\n\n    else\n        local port = ngx.var.port\n        local ok, err = sock:connect(\"127.0.0.1\", port)\n        if not ok then\n            ngx.say(\"failed to connect again: \", err)\n            return\n        end\n\n        ngx.say(\"connected again: \", ok)\n    end\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\nend\n--- request\nGET /t\n--- response_body_like eval\nqr/^(?:connected(?: again)?: 1\nrequest sent: 11\nreceived: OK\n){2}$/\n--- error_log\nlua reuse socket upstream ctx\n--- no_error_log\n[error]\n--- SKIP\n\n\n\n=== TEST 23: connect again immediately\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected again: \", ok)\n\n            local req = \"flush_all\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local line, err, part = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n\n            else\n                ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nconnected again: 1\nrequest sent: 11\nreceived: OK\nclose: 1 nil\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua reuse socket upstream\", \"lua tcp socket reconnect without shutting down\"]\n\n\n\n=== TEST 24: two sockets mix together\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port1 $TEST_NGINX_MEMCACHED_PORT;\n        set $port2 $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock1 = ngx.socket.tcp()\n            local sock2 = ngx.socket.tcp()\n\n            local port1 = ngx.var.port1\n            local port2 = ngx.var.port2\n\n            local ok, err = sock1:connect(\"127.0.0.1\", port1)\n            if not ok then\n                ngx.say(\"1: failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"1: connected: \", ok)\n\n            ok, err = sock2:connect(\"127.0.0.1\", port2)\n            if not ok then\n                ngx.say(\"2: failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"2: connected: \", ok)\n\n            local req1 = \"flush_all\\\\r\\\\n\"\n            local bytes, err = sock1:send(req1)\n            if not bytes then\n                ngx.say(\"1: failed to send request: \", err)\n                return\n            end\n            ngx.say(\"1: request sent: \", bytes)\n\n            local req2 = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            local bytes, err = sock2:send(req2)\n            if not bytes then\n                ngx.say(\"2: failed to send request: \", err)\n                return\n            end\n            ngx.say(\"2: request sent: \", bytes)\n\n            local line, err, part = sock1:receive()\n            if line then\n                ngx.say(\"1: received: \", line)\n\n            else\n                ngx.say(\"1: failed to receive a line: \", err, \" [\", part, \"]\")\n            end\n\n            line, err, part = sock2:receive()\n            if line then\n                ngx.say(\"2: received: \", line)\n\n            else\n                ngx.say(\"2: failed to receive a line: \", err, \" [\", part, \"]\")\n            end\n\n            ok, err = sock1:close()\n            ngx.say(\"1: close: \", ok, \" \", err)\n\n            ok, err = sock2:close()\n            ngx.say(\"2: close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\n1: connected: 1\n2: connected: 1\n1: request sent: 11\n2: request sent: 57\n1: received: OK\n2: received: HTTP/1.1 200 OK\n1: close: 1 nil\n2: close: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 25: send tables of string fragments (with integers too)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = {\"GET\", \" \", \"/foo\", \" HTTP/\", 1, \".\", 0, \"\\\\r\\\\n\",\n                         \"Host: localhost\\\\r\\\\n\", \"Connection: close\\\\r\\\\n\",\n                         \"\\\\r\\\\n\"}\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 26: send tables of string fragments (bad type \"nil\")\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = {\"GET\", \" \", \"/foo\", \" HTTP/\", nil, 1, \".\", 0, \"\\\\r\\\\n\",\n                         \"Host: localhost\\\\r\\\\n\", \"Connection: close\\\\r\\\\n\",\n                         \"\\\\r\\\\n\"}\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- ignore_response\n--- error_log\nbad argument #1 to 'send' (bad data type nil found)\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP/3 stream 0 reset by server#\n\n\n\n=== TEST 27: send tables of string fragments (bad type \"boolean\")\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = {\"GET\", \" \", \"/foo\", \" HTTP/\", true, 1, \".\", 0, \"\\\\r\\\\n\",\n                         \"Host: localhost\\\\r\\\\n\", \"Connection: close\\\\r\\\\n\",\n                         \"\\\\r\\\\n\"}\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- ignore_response\n--- error_log\nbad argument #1 to 'send' (bad data type boolean found)\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP/3 stream 0 reset by server#\n\n\n\n=== TEST 28: send tables of string fragments (bad type ngx.null)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = {\"GET\", \" \", \"/foo\", \" HTTP/\", ngx.null, 1, \".\", 0, \"\\\\r\\\\n\",\n                         \"Host: localhost\\\\r\\\\n\", \"Connection: close\\\\r\\\\n\",\n                         \"\\\\r\\\\n\"}\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- ignore_response\n--- error_log\nbad argument #1 to 'send' (bad data type userdata found)\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP/3 stream 0 reset by server#\n\n\n\n=== TEST 29: cosocket before location capture (tcpsock:send did not clear u->waiting)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"flush_all\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local line, err, part = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n\n            else\n                ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n\n            local resp = ngx.location.capture(\"/memc\")\n            if type(resp) ~= \"table\" then\n                ngx.say(\"bad resp: type \", type(resp), \": \", resp)\n                return\n            end\n\n            ngx.print(\"subrequest: \", resp.status, \", \", resp.body)\n        ';\n    }\n\n    location /memc {\n        set $memc_cmd flush_all;\n        memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT;\n    }\n--- request\nGET /t\n--- response_body eval\n\"connected: 1\nrequest sent: 11\nreceived: OK\nclose: 1 nil\nsubrequest: 200, OK\\r\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 30: CR in a line\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo \"foo\\r\\rbar\\rbaz\";\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 13\nreceived: Connection: close\nreceived: \nreceived: foobarbaz\nfailed to receive a line: closed []\nclose: nil closed\n--- no_error_log\n[error]\n--- SKIP\n\n\n\n=== TEST 31: receive(0)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local data, err, part = sock:receive(0)\n            if not data then\n                ngx.say(\"failed to receive(0): \", err)\n                return\n            end\n\n            ngx.say(\"receive(0): [\", data, \"]\")\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceive(0): []\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 32: send(\"\")\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local bytes, err = sock:send(\"\")\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"send(\\\\\"\\\\\"): \", bytes)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nsend(\"\"): 0\nclose: 1 nil\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 33: github issue #215: Handle the posted requests in lua cosocket api (failed to resolve)\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n\n    location = /sub {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"xxx\", 80)\n            if not ok then\n                ngx.say(\"failed to connect to xxx: \", err)\n                return\n            end\n            ngx.say(\"successfully connected to xxx!\")\n            sock:close()\n        ';\n    }\n\n    location = /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n\n--- stap\nF(ngx_resolve_name_done) {\n    println(\"resolve name done\")\n    #print_ubacktrace()\n}\n\n--- stap_out\nresolve name done\n\n--- response_body_like chop\n^failed to connect to xxx: xxx could not be resolved.*?Host not found\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 34: github issue #215: Handle the posted requests in lua cosocket api (successfully resolved)\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 5s;\n\n    location = /sub {\n        content_by_lua '\n            if not package.i then\n                package.i = 1\n            end\n\n            local servers = {\"openresty.org\", \"agentzh.org\", \"sregex.org\"}\n            local server = servers[package.i]\n            package.i = package.i + 1\n\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(server, 80)\n            if not ok then\n                ngx.say(\"failed to connect to \", server, \": \", err)\n                return\n            end\n            ngx.say(\"successfully connected to xxx!\")\n            sock:close()\n        ';\n    }\n\n    location = /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nsuccessfully connected to xxx!\n\n--- stap\nF(ngx_http_lua_socket_resolve_handler) {\n    println(\"lua socket resolve handler\")\n}\n\nF(ngx_http_lua_socket_tcp_conn_retval_handler) {\n    println(\"lua socket tcp connect retval handler\")\n}\n\nF(ngx_http_run_posted_requests) {\n    println(\"run posted requests\")\n}\n\n--- stap_out_like\nrun posted requests\nlua socket resolve handler\nrun posted requests\nlua socket tcp connect retval handler\nrun posted requests\n\n--- no_error_log\n[error]\n--- timeout: 10\n\n\n\n=== TEST 35: connection refused (tcp) - lua_socket_log_errors off\n--- config\n    location /test {\n        lua_socket_log_errors off;\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", 16787)\n            ngx.say(\"connect: \", ok, \" \", err)\n\n            local bytes\n            bytes, err = sock:send(\"hello\")\n            ngx.say(\"send: \", bytes, \" \", err)\n\n            local line\n            line, err = sock:receive()\n            ngx.say(\"receive: \", line, \" \", err)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n--- request\n    GET /test\n--- response_body\nconnect: nil connection refused\nsend: nil closed\nreceive: nil closed\nclose: nil closed\n--- no_error_log eval\n[qr/connect\\(\\) failed \\(\\d+: Connection refused\\)/]\n\n\n\n=== TEST 36: reread after a read time out happen (receive -> receive)\n--- config\n    server_tokens off;\n    lua_socket_read_timeout 100ms;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local line\n            line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n            else\n                ngx.say(\"failed to receive: \", err)\n\n                line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                end\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: timeout\nfailed to receive: timeout\n--- error_log\nlua tcp socket read timeout: 100\nlua tcp socket connect timeout: 60000\nlua tcp socket read timed out\n\n\n\n=== TEST 37: successful reread after a read time out happen (receive -> receive)\n--- no_http2\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", ngx.var.server_port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"GET /back HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\n\\\\r\\\\n\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local header, err = reader()\n            if not header then\n                ngx.say(\"failed to read the response header: \", err)\n                return\n            end\n\n            sock:settimeout(100)\n\n            local data, err, partial = sock:receive(100)\n            if data then\n                ngx.say(\"received: \", data)\n            else\n                ngx.say(\"failed to receive: \", err, \", partial: \", partial)\n\n                sock:settimeout(123)\n                ngx.sleep(0.1)\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n                ngx.say(\"received: \", line)\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n                ngx.say(\"received: \", line)\n            end\n        ';\n    }\n\n    location /back {\n        content_by_lua '\n            ngx.print(\"hi\")\n            ngx.flush(true)\n            ngx.sleep(0.2)\n            ngx.print(\"world\")\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\n\"failed to receive: timeout, partial: 2\\r\nhi\\r\n\nreceived: 5\nreceived: world\n\"\n--- error_log\nlua tcp socket read timed out\n--- no_error_log\n[alert]\n\n\n\n=== TEST 38: successful reread after a read time out happen (receive -> receiveuntil)\n--- no_http2\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", ngx.var.server_port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"GET /back HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\n\\\\r\\\\n\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local header, err = reader()\n            if not header then\n                ngx.say(\"failed to read the response header: \", err)\n                return\n            end\n\n            sock:settimeout(100)\n\n            local data, err, partial = sock:receive(100)\n            if data then\n                ngx.say(\"received: \", data)\n            else\n                ngx.say(\"failed to receive: \", err, \", partial: \", partial)\n\n                ngx.sleep(0.1)\n\n                sock:settimeout(123)\n                local reader = sock:receiveuntil(\"\\\\r\\\\n\")\n\n                local line, err = reader()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n                ngx.say(\"received: \", line)\n\n                local line, err = reader()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n                ngx.say(\"received: \", line)\n            end\n        ';\n    }\n\n    location /back {\n        content_by_lua '\n            ngx.print(\"hi\")\n            ngx.flush(true)\n            ngx.sleep(0.2)\n            ngx.print(\"world\")\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\n\"failed to receive: timeout, partial: 2\\r\nhi\\r\n\nreceived: 5\nreceived: world\n\"\n--- error_log\nlua tcp socket read timed out\n--- no_error_log\n[alert]\n\n\n\n=== TEST 39: successful reread after a read time out happen (receiveuntil -> receiveuntil)\n--- no_http2\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", ngx.var.server_port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"GET /back HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\n\\\\r\\\\n\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local header, err = reader()\n            if not header then\n                ngx.say(\"failed to read the response header: \", err)\n                return\n            end\n\n            sock:settimeout(100)\n\n            local reader = sock:receiveuntil(\"no-such-terminator\")\n            local data, err, partial = reader()\n            if data then\n                ngx.say(\"received: \", data)\n            else\n                ngx.say(\"failed to receive: \", err, \", partial: \", partial)\n\n                ngx.sleep(0.1)\n\n                sock:settimeout(123)\n\n                local reader = sock:receiveuntil(\"\\\\r\\\\n\")\n\n                local line, err = reader()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n                ngx.say(\"received: \", line)\n\n                local line, err = reader()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n                ngx.say(\"received: \", line)\n            end\n        ';\n    }\n\n    location /back {\n        content_by_lua '\n            ngx.print(\"hi\")\n            ngx.flush(true)\n            ngx.sleep(0.2)\n            ngx.print(\"world\")\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\n\"failed to receive: timeout, partial: 2\\r\nhi\\r\n\nreceived: 5\nreceived: world\n\"\n--- error_log\nlua tcp socket read timed out\n--- no_error_log\n[alert]\n\n\n\n=== TEST 40: successful reread after a read time out happen (receiveuntil -> receive)\n--- no_http2\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", ngx.var.server_port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"GET /back HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\n\\\\r\\\\n\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local header, err = reader()\n            if not header then\n                ngx.say(\"failed to read the response header: \", err)\n                return\n            end\n\n            sock:settimeout(100)\n\n            local reader = sock:receiveuntil(\"no-such-terminator\")\n            local data, err, partial = reader()\n            if data then\n                ngx.say(\"received: \", data)\n            else\n                ngx.say(\"failed to receive: \", err, \", partial: \", partial)\n\n                ngx.sleep(0.1)\n\n                sock:settimeout(123)\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n                ngx.say(\"received: \", line)\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n                ngx.say(\"received: \", line)\n            end\n        ';\n    }\n\n    location /back {\n        content_by_lua '\n            ngx.print(\"hi\")\n            ngx.flush(true)\n            ngx.sleep(0.2)\n            ngx.print(\"world\")\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\n\"failed to receive: timeout, partial: 2\\r\nhi\\r\n\nreceived: 5\nreceived: world\n\"\n--- error_log\nlua tcp socket read timed out\n--- no_error_log\n[alert]\n\n\n\n=== TEST 41: receive(0)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local data, err = sock:receive(0)\n            if not data then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"received: \", data)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nreceived: \nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 42: empty options table\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port, {})\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 43: u->coctx left over bug\n--- no_http2\n--- config\n    server_tokens off;\n    location = /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local ready = false\n            local fatal = false\n\n            local function f()\n                local line, err, part = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive the 1st line: \", err, \" [\", part, \"]\")\n                    fatal = true\n                    return\n                end\n                ready = true\n                ngx.sleep(1)\n            end\n\n            local st = ngx.thread.spawn(f)\n            while true do\n                if fatal then\n                    return\n                end\n\n                if not ready then\n                    ngx.sleep(0.01)\n                else\n                    break\n                end\n            end\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    -- ngx.say(\"received: \", line)\n\n                else\n                    -- ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n            ngx.exit(0)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.sleep(0.1) ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nclose: 1 nil\n--- no_error_log\n[error]\n--- error_log\nlua clean up the timer for pending ngx.sleep\n\n\n\n=== TEST 44: bad request tries to connect\n--- no_http2\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                test.new_sock()\n            end\n            local sock = test.get_sock()\n            local ok, err = sock:connect(\"127.0.0.1\", ngx.var.port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n            else\n                ngx.say(\"connected\")\n            end\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.tcp()\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^connected\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):7: bad request/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 45: bad request tries to receive\n--- no_http2\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:connect(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                else\n                    ngx.say(\"connected\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            sock:receive()\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.tcp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^connected\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):14: bad request/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 46: bad request tries to send\n--- no_http2\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:connect(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                else\n                    ngx.say(\"connected\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            sock:send(\"a\")\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.tcp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^connected\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):14: bad request/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 47: bad request tries to close\n--- no_http2\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:connect(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                else\n                    ngx.say(\"connected\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            sock:close()\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.tcp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^connected\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):14: bad request/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 48: bad request tries to set keepalive\n--- no_http2\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:connect(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                else\n                    ngx.say(\"connected\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            sock:setkeepalive()\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.tcp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^connected\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):14: bad request/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 49: bad request tries to receiveuntil\n--- no_http2\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:connect(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                else\n                    ngx.say(\"connected\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            local it, err = sock:receiveuntil(\"abc\")\n            if it then\n                it()\n            end\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.tcp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^connected\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):16: bad request/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 50: cosocket resolving aborted by coroutine yielding failures (require)\n--- http_config\n    lua_package_path \"$prefix/html/?.lua;;\";\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n\n--- config\n    location = /t {\n        content_by_lua '\n            package.loaded.myfoo = nil\n            require \"myfoo\"\n        ';\n    }\n--- request\n    GET /t\n--- user_files\n>>> myfoo.lua\nlocal sock = ngx.socket.tcp()\nlocal ok, err = sock:connect(\"agentzh.org\", 12345)\nif not ok then\n    ngx.log(ngx.ERR, \"failed to connect: \", err)\n    return\nend\n\n--- response_body_like: 500 Internal Server Error\n--- wait: 0.3\n--- error_code: 500\n--- error_log\nresolve name done\nruntime error: attempt to yield across C-call boundary\n--- no_error_log\n[alert]\n\n\n\n=== TEST 51: cosocket resolving aborted by coroutine yielding failures (xpcall err)\n--- http_config\n    lua_package_path \"$prefix/html/?.lua;;\";\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n\n--- config\n    location = /t {\n        content_by_lua '\n            local function f()\n                return error(1)\n            end\n            local function err()\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(\"agentzh.org\", 12345)\n                if not ok then\n                    ngx.log(ngx.ERR, \"failed to connect: \", err)\n                    return\n                end\n            end\n            xpcall(f, err)\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nok\n--- wait: 0.3\n--- error_log\nresolve name done\n--- no_error_log\n[error]\n[alert]\ncould not cancel\n\n\n\n=== TEST 52: tcp_nodelay on\n--- no_http2\n--- config\n    tcp_nodelay on;\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed []\nclose: 1 nil\n--- error_log\nlua socket tcp_nodelay\n--- no_error_log\n[error]\n\n\n\n=== TEST 53: tcp_nodelay off\n--- no_http2\n--- config\n    tcp_nodelay off;\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\nlua socket tcp_nodelay\n[error]\n\n\n\n=== TEST 54: IPv6\n--- http_config\n    server_tokens off;\n\n    server {\n        listen [::1]:$TEST_NGINX_SERVER_PORT;\n\n        location /foo {\n            content_by_lua 'ngx.say(\"foo\")';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"[::1]\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\n[error]\n--- skip_eval: 3: system(\"ping6 -c 1 ::1 >/dev/null 2>&1\") ne 0\n\n\n\n=== TEST 55: kill a thread with a connecting socket\n--- config\n    server_tokens off;\n    lua_socket_connect_timeout 1s;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t {\n        content_by_lua '\n            local sock\n\n            local thr = ngx.thread.spawn(function ()\n                sock = ngx.socket.tcp()\n                local ok, err = sock:connect(\"127.0.0.2\", 12345)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n            end)\n\n            ngx.sleep(0.002)\n            ngx.thread.kill(thr)\n            ngx.sleep(0.001)\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to setkeepalive: \", err)\n            else\n                ngx.say(\"setkeepalive: \", ok)\n            end\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nfailed to setkeepalive: closed\n--- error_log\nlua tcp socket connect timeout: 100\n--- timeout: 10\n\n\n\n=== TEST 56: reuse cleanup\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            for i = 1, 2 do\n                local ok, err = sock:connect(\"127.0.0.1\", port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send request: \", err)\n                    return\n                end\n\n                ngx.say(\"request sent: \", bytes)\n\n                while true do\n                    local line, err, part = sock:receive()\n                    if not line then\n                        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                        break\n                    end\n                end\n\n                ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end\n        }\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nfailed to receive a line: closed []\nclose: 1 nil\nconnected: 1\nrequest sent: 57\nfailed to receive a line: closed []\nclose: 1 nil\n--- error_log\nlua http cleanup reuse\n\n\n\n=== TEST 57: reuse cleanup in ngx.timer (fake_request)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            local total_send_bytes = 0\n            local port = ngx.var.port\n\n            local function network()\n                local sock = ngx.socket.tcp()\n\n                local ok, err = sock:connect(\"127.0.0.1\", port)\n                if not ok then\n                    ngx.log(ngx.ERR, \"failed to connect: \", err)\n                    return\n                end\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.log(ngx.ERR, \"failed to send request: \", err)\n                    return\n                end\n\n                total_send_bytes = total_send_bytes + bytes\n\n                while true do\n                    local line, err, part = sock:receive()\n                    if not line then\n                        break\n                    end\n                end\n\n                ok, err = sock:close()\n            end\n\n            local done = false\n\n            local function double_network()\n                network()\n                network()\n                done = true\n            end\n\n            local ok, err = ngx.timer.at(0, double_network)\n            if not ok then\n                ngx.say(\"failed to create timer: \", err)\n            end\n\n            local i = 1\n            while not done do\n                local time = 0.005 * i\n                if time > 0.1 then\n                    time = 0.1\n                end\n                ngx.sleep(time)\n                i = i + 1\n            end\n\n            collectgarbage(\"collect\")\n\n            ngx.say(\"total_send_bytes: \", total_send_bytes)\n        }\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\ntotal_send_bytes: 114\n--- error_log\nlua http cleanup reuse\n\n\n\n=== TEST 58: free cleanup in ngx.timer (without sock:close)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            local total_send_bytes = 0\n            local port = ngx.var.port\n\n            local function network()\n                local sock = ngx.socket.tcp()\n\n                local ok, err = sock:connect(\"127.0.0.1\", port)\n                if not ok then\n                    ngx.log(ngx.ERR, \"failed to connect: \", err)\n                    return\n                end\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.log(ngx.ERR, \"failed to send request: \", err)\n                    return\n                end\n\n                total_send_bytes = total_send_bytes + bytes\n\n                while true do\n                    local line, err, part = sock:receive()\n                    if not line then\n                        break\n                    end\n                end\n            end\n\n            local done = false\n\n            local function double_network()\n                network()\n                network()\n                done = true\n            end\n\n            local ok, err = ngx.timer.at(0, double_network)\n            if not ok then\n                ngx.say(\"failed to create timer: \", err)\n            end\n\n            local i = 1\n            while not done do\n                local time = 0.005 * i\n                if time > 0.1 then\n                    time = 0.1\n                end\n                ngx.sleep(time)\n                i = i + 1\n            end\n\n            collectgarbage(\"collect\")\n\n            ngx.say(\"total_send_bytes: \", total_send_bytes)\n        }\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\ntotal_send_bytes: 114\n--- no_error_log\n[error]\n\n\n\n=== TEST 59: reuse cleanup in subrequest\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        echo_location /tt;\n    }\n\n    location /tt {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            for i = 1, 2 do\n                local ok, err = sock:connect(\"127.0.0.1\", port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send request: \", err)\n                    return\n                end\n\n                ngx.say(\"request sent: \", bytes)\n\n                while true do\n                    local line, err, part = sock:receive()\n                    if not line then\n                        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                        break\n                    end\n                end\n\n                ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nfailed to receive a line: closed []\nclose: 1 nil\nconnected: 1\nrequest sent: 57\nfailed to receive a line: closed []\nclose: 1 nil\n--- error_log\nlua http cleanup reuse\n\n\n\n=== TEST 60: setkeepalive on socket already shutdown\n--- no_http2\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local ok, err = sock:close()\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to close socket: \", err)\n                return\n            end\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to setkeepalive: \", err)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\n--- error_log\nfailed to setkeepalive: closed\n\n\n\n=== TEST 61: options_table is nil\n--- no_http2\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port, nil)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"flush_all\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local line, err, part = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n\n            else\n                ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 11\nreceived: OK\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 62: resolver send query failing immediately in connect()\nthis case did not clear coctx->cleanup properly and would lead to memory invalid accesses.\n\nthis test case requires the following iptables rule to work properly:\n\nsudo iptables -I OUTPUT 1 -p udp --dport 10086 -j REJECT\n\n--- config\n    location /t {\n        resolver 127.0.0.1:10086 ipv6=off;\n        resolver_timeout 10ms;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            for i = 1, 3 do -- retry\n                local ok, err = sock:connect(\"www.google.com\", 80)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                end\n            end\n\n            ngx.say(\"hello!\")\n        }\n    }\n--- request\nGET /t\n--- response_body_like\nfailed to connect: www.google.com could not be resolved(?: \\(\\d+: Operation timed out\\))?\nfailed to connect: www.google.com could not be resolved(?: \\(\\d+: Operation timed out\\))?\nfailed to connect: www.google.com could not be resolved(?: \\(\\d+: Operation timed out\\))?\nhello!\n--- error_log eval\nqr{\\[alert\\] .*? send\\(\\) failed \\(\\d+: Operation not permitted\\) while resolving}\n\n\n\n=== TEST 63: the upper bound of port range should be 2^16 - 1\n--- config\n    location /t {\n        content_by_lua_block {\n            local sock, err = ngx.socket.connect(\"127.0.0.1\", 65536)\n            if not sock then\n                ngx.say(\"failed to connect: \", err)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\nfailed to connect: bad port number: 65536\n--- no_error_log\n[error]\n\n\n\n=== TEST 64: send boolean and nil\n--- no_http2\n--- config\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local function send(data)\n                local bytes, err = sock:send(data)\n                if not bytes then\n                    ngx.say(\"failed to send request: \", err)\n                    return\n                end\n            end\n\n            local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\nTest: \"\n            send(req)\n            send(true)\n            send(false)\n            send(nil)\n            send(\"\\r\\n\\r\\n\")\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n                else\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n        }\n    }\n\n    location /foo {\n        server_tokens off;\n        more_clear_headers Date;\n        echo $http_test;\n    }\n\n--- request\nGET /t\n--- response_body\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Connection: close\nreceived: \nreceived: truefalsenil\n--- no_error_log\n[error]\n\n\n\n=== TEST 65: receiveany method in cosocket\n--- no_http2\n--- config\n    server_tokens off;\n    location = /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(500)\n            assert(sock:connect(\"127.0.0.1\", ngx.var.port))\n            local req = {\n                'GET /foo HTTP/1.0\\r\\n',\n                'Host: localhost\\r\\n',\n                'Connection: close\\r\\n\\r\\n',\n            }\n            local ok, err = sock:send(req)\n            if not ok then\n                ngx.say(\"send request failed: \", err)\n                return\n            end\n\n            -- skip http header\n            while true do\n                local data, err, _ = sock:receive('*l')\n                if err then\n                    ngx.say('unexpected error occurs when receiving http head: ', err)\n                    return\n                end\n\n                if #data == 0 then -- read last line of head\n                    break\n                end\n            end\n\n            -- receive http body\n            while true do\n                local data, err = sock:receiveany(1024)\n                if err then\n                    if err ~= 'closed' then\n                        ngx.say('unexpected err: ', err)\n                    end\n                    break\n                end\n                ngx.say(data)\n            end\n\n            sock:close()\n        }\n    }\n\n    location = /foo {\n        content_by_lua_block {\n            local resp = {\n                '1',\n                '22',\n                'hello world',\n            }\n\n            local length = 0\n            for _, v in ipairs(resp) do\n                length = length + #v\n            end\n\n            -- flush http header\n            ngx.header['Content-Length'] = length\n            ngx.flush(true)\n            ngx.sleep(0.01)\n\n            -- send http body\n            for _, v in ipairs(resp) do\n                ngx.print(v)\n                ngx.flush(true)\n                ngx.sleep(0.01)\n            end\n        }\n    }\n\n--- request\nGET /t\n--- response_body\n1\n22\nhello world\n--- no_error_log\n[error]\n--- error_log\nlua tcp socket read any\n--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne \"\")\n\n\n\n=== TEST 66: receiveany send data after read side closed\n--- config\n    server_tokens off;\n    location = /t {\n        set $port $TEST_NGINX_RAND_PORT_1;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            sock:settimeout(500)\n            assert(sock:connect(\"127.0.0.1\", port))\n\n            while true do\n                local data, err = sock:receiveany(1024)\n                if err then\n                    if err ~= 'closed' then\n                        ngx.say('unexpected err: ', err)\n                        break\n                    end\n\n                    local data = \"send data after read side closed\"\n                    local bytes, err = sock:send(data)\n                    if not bytes then\n                        ngx.say(err)\n                    end\n\n                    break\n                end\n                ngx.say(data)\n            end\n\n            sock:close()\n        }\n    }\n\n--- request\nGET /t\n--- tcp_listen: $TEST_NGINX_RAND_PORT_1\n--- tcp_shutdown: 1\n--- tcp_query eval: \"send data after read side closed\"\n--- tcp_query_len: 32\n--- response_body\n--- no_error_log\n[error]\n\n\n\n=== TEST 67: receiveany with limited, max <= 0\n--- no_http2\n--- config\n    location = /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(500)\n            assert(sock:connect(\"127.0.0.1\", ngx.var.port))\n\n            local function receiveany_say_err(...)\n                local ok, err = pcall(sock.receiveany, sock, ...)\n                if not ok then\n                    ngx.say(err)\n                end\n            end\n\n\n            receiveany_say_err(0)\n            receiveany_say_err(-1)\n            receiveany_say_err()\n            receiveany_say_err(nil)\n        }\n    }\n\n--- response_body\nbad argument #2 to '?' (bad max argument)\nbad argument #2 to '?' (bad max argument)\nexpecting 2 arguments (including the object), but got 1\nbad argument #2 to '?' (bad max argument)\n--- request\nGET /t\n--- no_error_log\n[error]\n\n\n\n=== TEST 68: receiveany with limited, max is larger than data\n--- no_http2\n--- config\n    server_tokens off;\n    location = /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(500)\n            assert(sock:connect(\"127.0.0.1\", ngx.var.port))\n            local req = {\n                'GET /foo HTTP/1.0\\r\\n',\n                'Host: localhost\\r\\n',\n                'Connection: close\\r\\n\\r\\n',\n            }\n            local ok, err = sock:send(req)\n            if not ok then\n                ngx.say(\"send request failed: \", err)\n                return\n            end\n\n            while true do\n                local data, err, _ = sock:receive('*l')\n                if err then\n                    ngx.say('unexpected error occurs when receiving http head: ', err)\n                    return\n                end\n\n                if #data == 0 then -- read last line of head\n                    break\n                end\n            end\n\n            local data, err = sock:receiveany(128)\n            if err then\n                if err ~= 'closed' then\n                    ngx.say('unexpected err: ', err)\n                end\n            else\n                ngx.say(data)\n            end\n\n            sock:close()\n        }\n    }\n\n    location = /foo {\n        content_by_lua_block {\n            local resp = 'hello world'\n            local length = #resp\n\n            ngx.header['Content-Length'] = length\n            ngx.flush(true)\n            ngx.sleep(0.01)\n\n            ngx.print(resp)\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nhello world\n--- no_error_log\n[error]\n--- error_log\nlua tcp socket calling receiveany() method to read at most 128 bytes\n--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne \"\")\n\n\n\n=== TEST 69: receiveany with limited, max is smaller than data\n--- no_http2\n--- config\n    server_tokens off;\n    location = /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(500)\n            assert(sock:connect(\"127.0.0.1\", ngx.var.port))\n            local req = {\n                'GET /foo HTTP/1.0\\r\\n',\n                'Host: localhost\\r\\n',\n                'Connection: close\\r\\n\\r\\n',\n            }\n            local ok, err = sock:send(req)\n            if not ok then\n                ngx.say(\"send request failed: \", err)\n                return\n            end\n\n            while true do\n                local data, err, _ = sock:receive('*l')\n                if err then\n                    ngx.say('unexpected error occurs when receiving http head: ', err)\n                    return\n                end\n\n                if #data == 0 then -- read last line of head\n                    break\n                end\n            end\n\n            while true do\n                local data, err = sock:receiveany(7)\n                if err then\n                    if err ~= 'closed' then\n                        ngx.say('unexpected err: ', err)\n                    end\n                    break\n\n                else\n                    ngx.say(data)\n                end\n            end\n\n            sock:close()\n        }\n    }\n\n    location = /foo {\n        content_by_lua_block {\n            local resp = 'hello world'\n            local length = #resp\n\n            ngx.header['Content-Length'] = length\n            ngx.flush(true)\n            ngx.sleep(0.01)\n\n            ngx.print(resp)\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nhello w\norld\n--- no_error_log\n[error]\n--- error_log\nlua tcp socket calling receiveany() method to read at most 7 bytes\n--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne \"\")\n\n\n\n=== TEST 70: send tables of string fragments (with floating point number too)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = {\"GET\", \" \", \"/foo\", \" HTTP/\", 1, \".\", 0, \"\\r\\n\",\n                         \"Host: localhost\\r\\n\", \"Connection: close\\r\\n\",\n                         \"Foo: \", 3.1415926, \"\\r\\n\",\n                         \"\\r\\n\"}\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n\n    location /foo {\n        content_by_lua_block {\n            ngx.say(ngx.req.get_headers()[\"Foo\"])\n        }\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 73\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 10\nreceived: Connection: close\nreceived: \nreceived: 3.1415926\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 71: send numbers\nthe maximum number of significant digits is 14 in lua\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = {\"GET\", \" \", \"/foo\", \" HTTP/\", 1, \".\", 0, \"\\r\\n\",\n                         \"Host: localhost\\r\\n\", \"Connection: close\\r\\n\",\n                         \"Foo: \"}\n            -- req = \"OK\"\n\n            local total_bytes = 0;\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            total_bytes = total_bytes + bytes;\n\n            bytes, err = sock:send(3.14159265357939723846)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            total_bytes = total_bytes + bytes;\n\n            bytes, err = sock:send(31415926)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            total_bytes = total_bytes + bytes;\n\n            bytes, err = sock:send(\"\\r\\n\\r\\n\")\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            total_bytes = total_bytes + bytes;\n\n            ngx.say(\"request sent: \", total_bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n\n    location /foo {\n        content_by_lua_block {\n            ngx.say(ngx.req.get_headers()[\"Foo\"])\n        }\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 87\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 24\nreceived: Connection: close\nreceived: \nreceived: 3.141592653579431415926\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 72: port is not number\n--- config\n    server_tokens off;\n    location = /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(500)\n\n            local ok, err = sock:connect(\"127.0.0.1\")\n            if not ok then\n                ngx.say(\"connect failed: \", err)\n            end\n\n            local ok, err = sock:connect(\"127.0.0.1\", nil)\n            if not ok then\n                ngx.say(\"connect failed: \", err)\n            end\n\n            local ok, err = sock:connect(\"127.0.0.1\", {})\n            if not ok then\n                ngx.say(\"connect failed: \", err)\n            end\n\n            ngx.say(\"finish\")\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnect failed: missing the port number\nconnect failed: missing the port number\nconnect failed: missing the port number\nfinish\n--- no_error_log\n[error]\n\n\n\n=== TEST 73: reset the buffer pos when keepalive\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            for i = 1, 10\n            do\n                local sock = ngx.socket.tcp()\n                local port = ngx.var.port\n                local ok, err = sock:connect(\"127.0.0.1\", port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                local req = \"GET /hi HTTP/1.1\\r\\nHost: localhost\\r\\n\\r\\n\"\n\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send request: \", err)\n                    return\n                end\n\n                local line, err, part = sock:receive()\n                if not line then\n                    ngx.say(\"receive err: \", err)\n                    return\n                end\n\n                data, err = sock:receiveany(4096)\n                if not data then\n                    ngx.say(\"receiveany er: \", err)\n                    return\n                end\n\n                ok, err = sock:setkeepalive(10000, 32)\n                if not ok then\n                    ngx.say(\"reused times: \", i, \", setkeepalive err: \", err)\n                    return\n                end\n            end\n            ngx.say(\"END\")\n        }\n    }\n\n    location /hi {\n        keepalive_requests 3;\n        content_by_lua_block {\n            ngx.say(\"Hello\")\n        }\n\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\nreused times: 3, setkeepalive err: closed\n--- no_error_log\n[error]\n--- skip_eval: 3: $ENV{TEST_NGINX_EVENT_TYPE} && $ENV{TEST_NGINX_EVENT_TYPE} ne 'epoll'\n\n\n\n=== TEST 74: setkeepalive with TLSv1.3\n--- skip_openssl: 3: < 1.1.1\n--- stream_server_config\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        ssl_certificate     ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        ssl_protocols TLSv1.3;\n\n        content_by_lua_block {\n            local sock = assert(ngx.req.socket(true))\n            local data\n            while true do\n                data = assert(sock:receive())\n                assert(data == \"hello\")\n            end\n        }\n--- config\n    location /test {\n        lua_ssl_protocols TLSv1.3;\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local ok, err = sock:sslhandshake(false, nil, false)\n            if not ok then\n                ngx.say(\"failed to sslhandshake: \", err)\n                return\n            end\n\n            local ok, err = sock:send(\"hello\\n\")\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            -- sleep a while to make sure the NewSessionTicket message has arrived\n            ngx.sleep(1)\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to setkeepalive: \", err)\n            else\n                ngx.say(\"setkeepalive: \", ok)\n            end\n        }\n    }\n--- request\nGET /test\n--- response_body\nconnected: 1\nsetkeepalive: 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 75: getfd()\n--- no_http2\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;;\";\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local s, err = sock:getfd()\n            ngx.say(\"fd: \", s)\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body eval\nqr/fd: \\d+\nconnected: 1\nrequest sent: 57\nreceived: HTTP\\/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text\\/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed \\[\\]\nclose: 1 nil\n/ms\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/059-unix-socket.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 1);\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n\nno_long_string();\n#no_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: connection refused (unix domain socket)\n--- config\n    location /test {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"unix:/tmp/nosuchfile.sock\")\n            ngx.say(\"connect: \", ok, \" \", err)\n\n            local bytes\n            bytes, err = sock:send(\"hello\")\n            ngx.say(\"send: \", bytes, \" \", err)\n\n            local line\n            line, err = sock:receive()\n            ngx.say(\"receive: \", line, \" \", err)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n--- request\n    GET /test\n--- response_body\nconnect: nil no such file or directory\nsend: nil closed\nreceive: nil closed\nclose: nil closed\n--- error_log eval\nqr{\\[crit\\] .*? connect\\(\\) to unix:/tmp/nosuchfile\\.sock failed}\n\n\n\n=== TEST 2: invalid host argument\n--- config\n    location /test {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"/tmp/test-nginx.sock\")\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n    }\n--- request\n    GET /test\n--- response_body\nfailed to connect: missing the port number\n\n\n\n=== TEST 3: sanity\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        default_type 'text/plain';\n\n        server_tokens off;\n        location /foo {\n            content_by_lua 'ngx.say(\"foo\")';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /test {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                print(\"calling receive\")\n                local line, err = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err)\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n--- request\n    GET /test\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed\nclose: 1 nil\n\n\n\n=== TEST 4: ngx.socket.stream\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        default_type 'text/plain';\n\n        server_tokens off;\n        location /foo {\n            content_by_lua_block { ngx.say(\"foo\") }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /test {\n        content_by_lua_block {\n            local sock = ngx.socket.stream()\n            local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                print(\"calling receive\")\n                local line, err = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err)\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n--- request\n    GET /test\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed\nclose: 1 nil\n\n\n\n=== TEST 5: port will be ignored\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        default_type 'text/plain';\n\n        server_tokens off;\n        location /foo {\n            content_by_lua 'ngx.say(\"foo\")';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /test {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\", 80)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                print(\"calling receive\")\n                local line, err = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err)\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n--- request\n    GET /test\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed\nclose: 1 nil\n\n\n\n=== TEST 6: second parameter is nil\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        default_type 'text/plain';\n\n        server_tokens off;\n        location /foo {\n            content_by_lua 'ngx.say(\"foo\")';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /test {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\", nil)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                print(\"calling receive\")\n                local line, err = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err)\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n--- request\n    GET /test\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed\nclose: 1 nil\n"
  },
  {
    "path": "t/060-lua-memcached.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 1);\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\nmy $pwd = `pwd`;\nchomp $pwd;\n$ENV{TEST_NGINX_PWD} ||= $pwd;\n\n#master_on();\nworkers(1);\n#log_level('warn');\n#worker_connections(1014);\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- http_config\n    lua_package_path '$TEST_NGINX_PWD/t/lib/?.lua;;';\n--- config\n    location /test {\n        content_by_lua '\n            package.loaded[\"socket\"] = ngx.socket\n            local Memcached = require \"Memcached\"\n            Memcached.socket = ngx.socket\n\n            local memc = Memcached.Connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n\n            memc:set(\"some_key\", \"hello 1234\")\n            local data = memc:get(\"some_key\")\n            ngx.say(\"some_key: \", data)\n        ';\n    }\n--- request\n    GET /test\n--- response_body\nsome_key: hello 1234\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: raw memcached\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;;';\"\n--- config\n    location /t {\n        content_by_lua '\n            local memcached = require \"resty.memcached\"\n            local memc, err = memcached.connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n\n            local ok, err = memc:set(\"some_key\", \"hello 1234\")\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set some_key: \", err)\n                ngx.exit(500)\n            end\n\n            local data, err = memc:get(\"some_key\")\n            if not data and err then\n                ngx.log(ngx.ERR, \"failed to get some_key: \", err)\n                ngx.exit(500)\n            end\n\n            ngx.say(\"some_key: \", data)\n\n            local res, err = memc:set_keepalive()\n            if not res then\n                ngx.say(\"failed to set keepalive: \", err)\n                return\n            end\n        ';\n    }\n--- user_files\n>>> resty/memcached.lua\nmodule(\"resty.memcached\", package.seeall)\n\nlocal mt = { __index = resty.memcached }\nlocal sub = string.sub\nlocal escape_uri = ngx.escape_uri\nlocal socket_connect = ngx.socket.connect\nlocal match = string.match\n\nfunction connect(...)\n    local sock, err = socket_connect(...)\n    return setmetatable({ sock = sock }, mt)\nend\n\nfunction get(self, key)\n    local cmd = \"get \" .. escape_uri(key) .. \"\\r\\n\"\n    local bytes, err = self.sock:send(cmd)\n    if not bytes then\n        return nil, err\n    end\n\n    local line, err = self.sock:receive()\n    if line == 'END' then\n        return nil, nil\n    end\n\n    local flags, len = match(line, [[^VALUE %S+ (%d+) (%d+)]])\n    if not flags then\n        return nil, \"bad response: \" .. line\n    end\n\n    print(\"size: \", size, \", flags: \", len)\n\n    local data, err = self.sock:receive(len)\n    if not data then\n        return nil, err\n    end\n\n    line, err = self.sock:receive(2) -- discard the trailing CRLF\n    if not line then\n        return nil, nil, \"failed to receive CRLF: \" .. (err or \"\")\n    end\n\n    line, err = self.sock:receive() -- discard \"END\\r\\n\"\n    if not line then\n        return nil, nil, \"failed to receive END CRLF: \" .. (err or \"\")\n    end\n\n    return data\nend\n\nfunction set(self, key, value, exptime, flags)\n    if not exptime then\n        exptime = 0\n    end\n\n    if not flags then\n        flags = 0\n    end\n\n    local cmd = table.concat({\"set \", escape_uri(key), \" \", flags, \" \", exptime, \" \", #value, \"\\r\\n\", value, \"\\r\\n\"}, \"\")\n\n    local bytes, err = self.sock:send(cmd)\n    if not bytes then\n        return nil, err\n    end\n\n    local data, err = self.sock:receive()\n    if sub(data, 1, 6) == \"STORED\" then\n        return true\n    end\n\n    return false, err\nend\n\nfunction set_keepalive(self)\n    return self.sock:setkeepalive(0, 100)\nend\n--- request\n    GET /t\n--- response_body\nsome_key: hello 1234\n--- no_error_log\n[error]\n--- error_log\nlua reuse free buf memory\n"
  },
  {
    "path": "t/061-lua-redis.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n$ENV{TEST_NGINX_REDIS_PORT} ||= 6379;\n\n#log_level \"warn\";\n#worker_connections(1024);\n#master_on();\n\nmy $pwd = `pwd`;\nchomp $pwd;\n$ENV{TEST_NGINX_PWD} ||= $pwd;\n\nour $LuaCpath = $ENV{LUA_CPATH} ||\n    '/usr/local/openresty-debug/lualib/?.so;/usr/local/openresty/lualib/?.so;;';\n\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- http_config\n    lua_package_path '$TEST_NGINX_PWD/t/lib/?.lua;;';\n--- config\n    location /test {\n        content_by_lua '\n            package.loaded[\"socket\"] = ngx.socket\n            local Redis = require \"Redis\"\n\n            local redis = Redis.connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n\n            redis:set(\"some_key\", \"hello 1234\")\n            local data = redis:get(\"some_key\")\n            ngx.say(\"some_key: \", data)\n        ';\n    }\n--- request\n    GET /test\n--- response_body\nsome_key: hello 1234\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: coroutine-based pub/sub\n--- http_config eval\nqq{\n    lua_package_path '\\$TEST_NGINX_PWD/t/lib/?.lua;;';\n    lua_package_cpath '$::LuaCpath';\n}\n--- config\n    location /test {\n        content_by_lua '\n            package.loaded[\"socket\"] = ngx.socket\n            local Redis = require \"Redis\"\n\n            local ljson = require \"ljson\"\n\n            local r1 = Redis.connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n\n            local r2 = Redis.connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n\n            local loop = r2:pubsub({ subscribe = \"foo\" })\n            local msg, abort = loop()\n            ngx.say(\"msg type: \", type(msg))\n            ngx.say(\"abort: \", type(abort))\n\n            if msg then\n                ngx.say(\"msg: \", ljson.encode(msg))\n            end\n\n            for i = 1, 3 do\n                r1:publish(\"foo\", \"test \" .. i)\n                msg, abort = loop()\n                if msg then\n                    ngx.say(\"msg: \", ljson.encode(msg))\n                end\n                ngx.say(\"abort: \", type(abort))\n            end\n\n            abort()\n\n            msg, abort = loop()\n            ngx.say(\"msg type: \", type(msg))\n        ';\n    }\n--- stap2\nglobal ids, cur\n\nfunction gen_id(k) {\n    if (ids[k]) return ids[k]\n    ids[k] = ++cur\n    return cur\n}\n\nF(ngx_http_handler) {\n    delete ids\n    cur = 0\n}\n\n/*\nprobe process(\"/usr/local/openresty-debug/luajit/lib/libluajit-5.1.so.2\").function(\"lua_yield\") {\n    id = gen_id($L)\n    printf(\"raw lua yield %d\\n\", id)\n    #print_ubacktrace()\n}\n\nprobe process(\"/usr/local/openresty-debug/luajit/lib/libluajit-5.1.so.2\").function(\"lua_resume\") {\n    id = gen_id($L)\n    printf(\"raw lua resume %d\\n\", id)\n}\n*/\n\n/*\nF(ngx_http_lua_run_thread) {\n    id = gen_id($ctx->cur_co)\n    printf(\"run thread %d\\n\", id)\n}\n*/\n\nM(http-lua-user-coroutine-resume) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"resume %x in %x\\n\", c, p)\n}\n\nM(http-lua-entry-coroutine-yield) {\n    println(\"entry coroutine yield\")\n}\n\nF(ngx_http_lua_coroutine_yield) {\n    printf(\"yield %x\\n\", gen_id($L))\n}\n\n/*\nF(ngx_http_lua_coroutine_resume) {\n    printf(\"resume %x\\n\", gen_id($L))\n}\n*/\n\nM(http-lua-user-coroutine-yield) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"yield %x in %x\\n\", c, p)\n}\n\nF(ngx_http_lua_atpanic) {\n    printf(\"lua atpanic(%d):\", gen_id($L))\n    print_ubacktrace();\n}\n\nM(http-lua-user-coroutine-create) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"create %x in %x\\n\", c, p)\n}\n\nF(ngx_http_lua_ngx_exec) { println(\"exec\") }\n\nF(ngx_http_lua_ngx_exit) { println(\"exit\") }\n\n--- request\n    GET /test\n--- response_body\nmsg type: table\nabort: function\nmsg: {\"channel\":\"foo\",\"kind\":\"subscribe\",\"payload\":1}\nmsg: {\"channel\":\"foo\",\"kind\":\"message\",\"payload\":\"test 1\"}\nabort: function\nmsg: {\"channel\":\"foo\",\"kind\":\"message\",\"payload\":\"test 2\"}\nabort: function\nmsg: {\"channel\":\"foo\",\"kind\":\"message\",\"payload\":\"test 3\"}\nabort: function\nmsg type: nil\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/062-count.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(4);\n#log_level('warn');\nno_root_location();\n\n#repeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\nour $HtmlDir = html_dir;\n\n#$ENV{LUA_CPATH} = \"/usr/local/openresty/lualib/?.so;\" . $ENV{LUA_CPATH};\n\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: entries under ngx. (content by lua)\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                for k, v in pairs(ngx) do\n                    n = n + 1\n                end\n                ngx.say(\"ngx: \", n)\n            ';\n        }\n--- request\nGET /test\n--- response_body\nngx: 117\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: entries under ngx. (set by lua)\n--- config\n        location = /test {\n            set_by_lua $n '\n                local n = 0\n                for k, v in pairs(ngx) do\n                    n = n + 1\n                end\n                return n;\n            ';\n            echo $n;\n        }\n--- request\nGET /test\n--- response_body\n117\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: entries under ngx. (header filter by lua)\n--- config\n        location = /test {\n            set $n '';\n\n            content_by_lua '\n                ngx.send_headers()\n                ngx.say(\"n = \", ngx.var.n)\n            ';\n\n            header_filter_by_lua '\n                local n = 0\n                for k, v in pairs(ngx) do\n                    n = n + 1\n                end\n\n                ngx.var.n = n\n            ';\n        }\n--- request\nGET /test\n--- response_body\nn = 117\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: entries under ndk. (content by lua)\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                for k, v in pairs(ndk) do\n                    n = n + 1\n                end\n                ngx.say(\"n = \", n)\n            ';\n        }\n--- request\nGET /test\n--- response_body\nn = 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: entries under ngx.req (content by lua)\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                for k, v in pairs(ngx.req) do\n                    n = n + 1\n                end\n                ngx.say(\"n = \", n)\n            ';\n        }\n--- request\nGET /test\n--- response_body\nn = 23\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: entries under ngx.req (set by lua)\n--- config\n        location = /test {\n            set_by_lua $n '\n                local n = 0\n                for k, v in pairs(ngx.req) do\n                    n = n + 1\n                end\n                return n\n            ';\n\n            echo \"n = $n\";\n        }\n--- request\nGET /test\n--- response_body\nn = 23\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: entries under ngx.req (header filter by lua)\n--- config\n        location = /test {\n            set $n '';\n\n            header_filter_by_lua '\n                local n = 0\n                for k, v in pairs(ngx.req) do\n                    n = n + 1\n                end\n                ngx.var.n = n\n            ';\n\n            content_by_lua '\n                ngx.send_headers()\n                ngx.say(\"n = \", ngx.var.n)\n            ';\n        }\n--- request\nGET /test\n--- response_body\nn = 23\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: entries under ngx.location\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                for k, v in pairs(ngx.location) do\n                    n = n + 1\n                end\n                ngx.say(\"n = \", n)\n            ';\n        }\n--- request\nGET /test\n--- response_body\nn = 2\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: entries under ngx.socket\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                for k, v in pairs(ngx.socket) do\n                    n = n + 1\n                end\n                ngx.say(\"n = \", n)\n            ';\n        }\n--- request\nGET /test\n--- response_body\nn = 4\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: entries under ngx._tcp_meta\n--- SKIP\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                for k, v in pairs(ngx._tcp_meta) do\n                    n = n + 1\n                end\n                ngx.say(\"n = \", n)\n            ';\n        }\n--- request\nGET /test\n--- response_body\nn = 10\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: entries under the metatable of req sockets\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                local sock, err = ngx.req.socket()\n                if not sock then\n                    ngx.say(\"failed to get the request socket: \", err)\n                end\n\n                for k, v in pairs(getmetatable(sock)) do\n                    n = n + 1\n                end\n                ngx.say(\"n = \", n)\n            ';\n        }\n--- request\nPOST /test\nhello world\n--- response_body\nn = 9\n--- no_error_log\n[error]\n--- skip_eval: 3: $ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 12: shdict metatable\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            local mt = dogs.__index\n            local n = 0\n            for k, v in pairs(mt) do\n                n = n + 1\n            end\n            ngx.say(\"n = \", n)\n        ';\n    }\n--- request\nGET /test\n--- response_body\nn = 22\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: entries under ngx. (log by lua)\n--- config\n    location = /t {\n        log_by_lua '\n            local n = 0\n            for k, v in pairs(ngx) do\n                n = n + 1\n            end\n            ngx.log(ngx.ERR, \"ngx. entry count: \", n)\n        ';\n    }\n--- request\nGET /t\n--- response_body_like: 404 Not Found\n--- error_code: 404\n--- error_log\nngx. entry count: 117\n\n\n\n=== TEST 14: entries under ngx.timer\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                for k, v in pairs(ngx.timer) do\n                    n = n + 1\n                end\n                ngx.say(\"n = \", n)\n            ';\n        }\n--- request\nGET /test\n--- response_body\nn = 4\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: entries under ngx.config\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                for k, v in pairs(ngx.config) do\n                    n = n + 1\n                end\n                ngx.say(\"n = \", n)\n            ';\n        }\n--- request\nGET /test\n--- response_body\nn = 6\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: entries under ngx.re\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                for k, v in pairs(ngx.re) do\n                    n = n + 1\n                end\n                ngx.say(\"n = \", n)\n            ';\n        }\n--- request\nGET /test\n--- response_body\nn = 5\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: entries under coroutine. (content by lua)\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                for k, v in pairs(coroutine) do\n                    n = n + 1\n                end\n                ngx.say(\"coroutine: \", n)\n            ';\n        }\n--- request\nGET /test\n--- stap2\nglobal c\nprobe process(\"$LIBLUA_PATH\").function(\"rehashtab\") {\n    c++\n    printf(\"rehash: %d\\n\", c)\n}\n--- stap_out2\n3\n--- response_body\ncoroutine: 16\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: entries under ngx.thread. (content by lua)\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                for k, v in pairs(ngx.thread) do\n                    n = n + 1\n                end\n                ngx.say(\"thread: \", n)\n            ';\n        }\n--- request\nGET /test\n--- stap2\nglobal c\nprobe process(\"$LIBLUA_PATH\").function(\"rehashtab\") {\n    c++\n    printf(\"rehash: %d\\n\", c)\n}\n--- stap_out2\n--- response_body\nthread: 3\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: entries under ngx.worker\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                for k, v in pairs(ngx.worker) do\n                    n = n + 1\n                end\n                ngx.say(\"worker: \", n)\n            ';\n        }\n--- request\nGET /test\n--- response_body\nworker: 5\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: entries under the metatable of tcp sockets\n--- config\n        location = /test {\n            content_by_lua_block {\n                local n = 0\n                local sock = ngx.socket.tcp()\n                for k, v in pairs(getmetatable(sock)) do\n                    n = n + 1\n                end\n                ngx.say(\"n = \", n)\n            }\n        }\n--- request\nGET /test\n--- response_body\nn = 20\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: entries under the metatable of udp sockets\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                local sock = ngx.socket.udp()\n                for k, v in pairs(getmetatable(sock)) do\n                    n = n + 1\n                end\n                ngx.say(\"n = \", n)\n            ';\n        }\n--- request\nGET /test\n--- response_body\nn = 7\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: entries under the metatable of req raw sockets\n--- config\n        location = /test {\n            content_by_lua '\n                local n = 0\n                ngx.req.read_body()\n                local sock, err = ngx.req.socket(true)\n                if not sock then\n                    ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                    return\n                end\n\n                for k, v in pairs(getmetatable(sock)) do\n                    n = n + 1\n                end\n\n                local ok, err = sock:send(\"HTTP/1.1 200 OK\\\\r\\\\nContent-Length: 7\\\\r\\\\n\\\\r\\\\nn = \"..n..\"\\\\n\")\n                if not ok then\n                    ngx.log(ngx.ERR, \"failed to send: \", err)\n                    return\n                end\n            ';\n        }\n--- request\nGET /test\n--- response_body\nn = 10\n--- no_error_log\n[error]\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 23: entries under the req raw sockets\n--- config\n        location = /test {\n            content_by_lua_block {\n                local narr = 0\n                local nrec = 0\n                ngx.req.read_body()\n                local sock, err = ngx.req.socket(true)\n                if not sock then\n                    ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                    return\n                end\n                sock:settimeouts(1000, 2000, 3000)\n                for k, v in ipairs(sock) do\n                    narr = narr + 1\n                end\n                for k, v in pairs(sock) do\n                    nrec = nrec + 1\n                end\n                -- include '__index'\n                nrec = nrec - narr + 1\n\n                local ok, err = sock:send(\"HTTP/1.1 200 OK\\r\\n\\r\\nnarr = \"..narr..\"\\nnrec = \"..nrec..\"\\n\")\n                if not ok then\n                    ngx.log(ngx.ERR, \"failed to send: \", err)\n                    return\n                end\n            }\n        }\n--- request\nGET /test\n--- response_body\nnarr = 2\nnrec = 3\n--- no_error_log\n[error]\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 24: entries under the req sockets\n--- config\n        location = /test {\n            content_by_lua_block {\n                local narr = 0\n                local nrec = 0\n                local sock, err = ngx.req.socket()\n                if not sock then\n                    ngx.log(ngx.ERR, \"server: failed to get req socket: \", err)\n                    return\n                end\n                sock:settimeouts(1000, 2000, 3000)\n                for k, v in ipairs(sock) do\n                    narr = narr + 1\n                end\n                for k, v in pairs(sock) do\n                    nrec = nrec + 1\n                end\n                -- include '__index'\n                nrec = nrec - narr + 1\n\n                ngx.say(\"narr = \"..narr..\"\\nnrec = \"..nrec)\n            }\n        }\n--- request\nPOST /test\nhello world\n--- response_body\nnarr = 2\nnrec = 3\n--- no_error_log\n[error]\n--- skip_eval: 3: $ENV{TEST_NGINX_USE_HTTP3}\n"
  },
  {
    "path": "t/063-abort.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\nworker_connections(1014);\n#master_on();\n#workers(4);\n#log_level('warn');\nno_root_location();\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 2);\n\nour $HtmlDir = html_dir;\n\n#$ENV{LUA_CPATH} = \"/usr/local/openresty/lualib/?.so;\" . $ENV{LUA_CPATH};\n\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: ngx.exit(400) should abort print\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /memc_query {\n            internal;\n            set               $memc_cmd     $arg_cmd;\n            set_unescape_uri  $memc_key     $arg_key;\n            set_unescape_uri  $memc_value   $arg_value;\n            set $memc_exptime $arg_exptime;\n\n            memc_cmds_allowed get set add delete;\n            memc_pass 127.0.0.1:11211;\n        }\n\n        location = /test {\n            content_by_lua_file html/test.lua;\n        }\n--- user_files\n>>> test.lua\nlocal memd = require 'memd'\nngx.exit(400)\nlocal res = memd.query( { cmd = 'get', key = id } )\n>>> memd.lua\nmodule('memd', package.seeall)\n\nlocal URL = '/memc_query'\nlocal capture = ngx.location.capture\n\nfunction query(arg)\n    if type(arg) ~= 'table' then\n        return nil\n    end\n\n    print(\"HELLO WORLD\")\n    return capture(URL, { args = arg } )\nend\n--- request\nGET /test?a\n--- response_body_like: 400 Bad Request\n--- no_error_log eval\n[\"lua print: HELLO WORLD\", q{the \"$memc_key\" variable is not set}]\n--- error_code: 400\n\n\n\n=== TEST 2: ngx.exit(400) should abort ngx.log\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /memc_query {\n            internal;\n            set               $memc_cmd     $arg_cmd;\n            set_unescape_uri  $memc_key     $arg_key;\n            set_unescape_uri  $memc_value   $arg_value;\n            set $memc_exptime $arg_exptime;\n\n            memc_cmds_allowed get set add delete;\n            memc_pass 127.0.0.1:11211;\n        }\n\n        location = /test {\n            content_by_lua_file html/test.lua;\n        }\n--- user_files\n>>> test.lua\nlocal memd = require 'memd'\nngx.exit(400)\nlocal res = memd.query( { cmd = 'get', key = id } )\n>>> memd.lua\nmodule('memd', package.seeall)\n\nlocal URL = '/memc_query'\nlocal capture = ngx.location.capture\nlocal log = ngx.log\nlocal level = ngx.ERR\n\nfunction query(arg)\n    if type(arg) ~= 'table' then\n        return nil\n    end\n\n    log(level, \"HELLO WORLD\")\n    return capture(URL, { args = arg } )\nend\n--- request\nGET /test?a\n--- response_body_like: 400 Bad Request\n--- no_error_log eval\n[\"HELLO WORLD\", q{the \"$memc_key\" variable is not set}]\n--- error_code: 400\n\n\n\n=== TEST 3: ngx.exit(400) should abort ngx.location.capture\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /memc_query {\n            internal;\n            set               $memc_cmd     $arg_cmd;\n            set_unescape_uri  $memc_key     $arg_key;\n            set_unescape_uri  $memc_value   $arg_value;\n            set $memc_exptime $arg_exptime;\n\n            memc_cmds_allowed get set add delete;\n            memc_pass 127.0.0.1:11211;\n        }\n\n        location = /test {\n            content_by_lua_file html/test.lua;\n        }\n--- user_files\n>>> test.lua\nlocal memd = require 'memd'\nngx.exit(400)\nlocal res = memd.query( { cmd = 'get', key = id } )\n>>> memd.lua\nmodule('memd', package.seeall)\n\nlocal URL = '/memc_query'\nlocal capture = ngx.location.capture\n\nfunction query(arg)\n    if type(arg) ~= 'table' then\n        return nil\n    end\n\n    return capture(URL, { args = arg } )\nend\n--- request\nGET /test?a\n--- response_body_like: 400 Bad Request\n--- no_error_log\nthe \"$memc_key\" variable is not set\n--- error_code: 400\n\n\n\n=== TEST 4: ngx.exit(400) should abort ngx.location.capture_multi\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /memc_query {\n            internal;\n            set               $memc_cmd     $arg_cmd;\n            set_unescape_uri  $memc_key     $arg_key;\n            set_unescape_uri  $memc_value   $arg_value;\n            set $memc_exptime $arg_exptime;\n\n            memc_cmds_allowed get set add delete;\n            memc_pass 127.0.0.1:11211;\n        }\n\n        location = /test {\n            content_by_lua_file html/test.lua;\n        }\n--- user_files\n>>> test.lua\nlocal memd = require 'memd'\nngx.exit(400)\nlocal res = memd.query( { cmd = 'get', key = id } )\n>>> memd.lua\nmodule('memd', package.seeall)\n\nlocal URL = '/memc_query'\nlocal capture_multi = ngx.location.capture_multi\n\nfunction query(arg)\n    if type(arg) ~= 'table' then\n        return nil\n    end\n\n    return capture_multi{ {URL, { args = arg }} }\nend\n--- request\nGET /test?a\n--- response_body_like: 400 Bad Request\n--- no_error_log\nthe \"$memc_key\" variable is not set\n--- error_code: 400\n\n\n\n=== TEST 5: ngx.exit(400) should abort ngx.redirect\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                ngx.exit(400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go()\n    ngx.redirect(\"/blah\")\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nlua redirect to \"/blah\" with code 302\n--- error_code: 400\n\n\n\n=== TEST 6: ngx.exit(400) should abort ngx.exit\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                ngx.exit(400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go()\n    ngx.exit(503)\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nlua exit with code 503\n--- error_code: 400\n\n\n\n=== TEST 7: ngx.exit(400) should abort ngx.exec\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                ngx.exit(400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go()\n    ngx.exec(\"/blah\")\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nlua exec \"/blah?\"\n--- error_code: 400\n\n\n\n=== TEST 8: ngx.exit(400) should abort ngx.send_headers\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                ngx.exit(400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go()\n    ngx.send_headers()\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nlua send headers\n--- error_code: 400\n\n\n\n=== TEST 9: ngx.exit(400) should abort ngx.print\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                ngx.exit(400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go()\n    ngx.print(\"HELLO WORLD\")\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nlua print response\n--- error_code: 400\n\n\n\n=== TEST 10: ngx.exit(400) should abort ngx.say\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                ngx.exit(400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go()\n    ngx.say(\"HELLO WORLD\")\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nlua say response\n--- error_code: 400\n\n\n\n=== TEST 11: ngx.exit(400) should abort ngx.flush\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                ngx.exit(400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go()\n    ngx.flush()\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nlua flush asynchronously\n--- error_code: 400\n\n\n\n=== TEST 12: ngx.exit(400) should abort ngx.eof\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                ngx.exit(400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go()\n    ngx.eof()\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nlua send eof\n--- error_code: 400\n\n\n\n=== TEST 13: ngx.exit(400) should abort ngx.re.match\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                ngx.exit(400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go()\n    ngx.re.match(\"a\", \"a\", \"jo\")\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nlua compiling match regex \"a\" with options \"jo\"\n--- error_code: 400\n\n\n\n=== TEST 14: ngx.exit(400) should abort ngx.re.gmatch\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                ngx.exit(400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go()\n    ngx.re.gmatch(\"a\", \"a\", \"jo\")\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nlua compiling gmatch regex \"a\" with options \"jo\"\n--- error_code: 400\n\n\n\n=== TEST 15: ngx.exit(400) should abort ngx.re.sub\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                ngx.exit(400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go()\n    ngx.re.sub(\"a\", \"a\", \"\", \"jo\")\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nlua compiling sub regex \"a\" with options \"jo\"\n--- error_code: 400\n\n\n\n=== TEST 16: ngx.exit(400) should abort ngx.re.gsub\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                ngx.exit(400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go()\n    ngx.re.gsub(\"a\", \"a\", \"\", \"jo\")\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nlua compiling gsub regex \"a\" with options \"jo\"\n--- error_code: 400\n\n\n\n=== TEST 17: ngx.exit(400) should abort ngx.shared.DICT (set)\n--- http_config eval\n    \"lua_shared_dict dogs 1m; lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                local dogs = ngx.shared.dogs\n                print(\"foo = \", dogs:get(\"foo\"))\n                dogs:set(\"foo\", 32)\n                ngx.exit(400)\n                test.go(dogs)\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go(dogs)\n    dogs:set(\"foo\", 56)\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nfoo = 56\n--- error_code: 400\n\n\n\n=== TEST 18: ngx.exit(400) should abort ngx.shared.DICT (replace)\n--- http_config eval\n    \"lua_shared_dict dogs 1m; lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                local dogs = ngx.shared.dogs\n                print(\"foo = \", dogs:get(\"foo\"))\n                dogs:set(\"foo\", 32)\n                ngx.exit(400)\n                test.go(dogs)\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go(dogs)\n    dogs:replace(\"foo\", 56)\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nfoo = 56\n--- error_code: 400\n\n\n\n=== TEST 19: ngx.exit(400) should abort ngx.shared.DICT (incr)\n--- http_config eval\n    \"lua_shared_dict dogs 1m; lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                local dogs = ngx.shared.dogs\n                print(\"foo = \", dogs:get(\"foo\"))\n                dogs:set(\"foo\", 32)\n                ngx.exit(400)\n                test.go(dogs)\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go(dogs)\n    dogs:incr(\"foo\", 56)\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nfoo = 88\n--- error_code: 400\n\n\n\n=== TEST 20: ngx.exit(400) should abort ngx.shared.DICT (get)\n--- http_config eval\n    \"lua_shared_dict dogs 1m; lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                local dogs = ngx.shared.dogs\n                dogs:set(\"foo\", 32)\n                ngx.exit(400)\n                test.go(dogs)\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nfunction go(dogs)\n    dogs:get(\"foo\")\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nfetching key \"foo\" in shared dict \"dogs\"\n--- error_code: 400\n\n\n\n=== TEST 21: ngx.exit(400) should skip os.execute\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                ngx.exit(400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nlocal exec = os.execute\n\nfunction go()\n    exec(\"sleep 5\")\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- error_code: 400\n--- no_error_log\n[error]\n--- timeout: 2\n\n\n\n=== TEST 22: ngx.exit(400) should break pcall and skip os.execute\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                pcall(ngx.exit, 400)\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nlocal exec = os.execute\n\nfunction go()\n    exec(\"sleep 5\")\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- no_error_log\nfetching key \"foo\" in shared dict \"dogs\"\n--- error_code: 400\n--- timeout: 2\n\n\n\n=== TEST 23: ngx.exit(400) should break pcall and skip os.execute (all in user module)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nlocal exec = os.execute\n\nfunction go()\n    pcall(ngx.exit, 400)\n    exec(\"sleep 5\")\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- error_code: 400\n--- no_error_log\n[error]\n--- timeout: 2\n\n\n\n=== TEST 24: ngx.redirect() should break pcall and skip os.execute (all in user module)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nlocal exec = os.execute\n\nfunction go()\n    pcall(ngx.redirect, \"/blah\")\n    exec(\"sleep 5\")\nend\n--- request\nGET /test\n--- response_body_like: 302 Found\n--- no_error_log\n[error]\n--- error_code: 302\n--- timeout: 2\n\n\n\n=== TEST 25: ngx.redirect() should skip os.execute (all in user module)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nlocal exec = os.execute\n\nfunction go()\n    ngx.redirect(\"/blah\")\n    exec(\"sleep 5\")\nend\n--- request\nGET /test\n--- response_body_like: 302 Found\n--- no_error_log\n[error]\n--- error_code: 302\n--- timeout: 2\n\n\n\n=== TEST 26: ngx.exec() should break pcall and skip os.execute (all in user module)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                test.go()\n            ';\n        }\n        location = /foo {\n            echo foo;\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nlocal exec = os.execute\n\nfunction go()\n    pcall(ngx.exec, \"/foo\")\n    exec(\"sleep 5\")\nend\n--- request\nGET /test\n--- response_body\nfoo\n--- no_error_log\n[error]\n--- timeout: 2\n\n\n\n=== TEST 27: ngx.exec() should skip os.execute (all in user module)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                test.go()\n            ';\n        }\n        location = /foo {\n            echo foo;\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nlocal exec = os.execute\n\nfunction go()\n    ngx.exec(\"/foo\")\n    exec(\"sleep 5\")\nend\n--- request\nGET /test\n--- response_body\nfoo\n--- no_error_log\n[error]\n--- timeout: 2\n\n\n\n=== TEST 28: ngx.set_uri(uri, true) should break pcall and skip os.execute (all in user module)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            rewrite_by_lua '\n                local test = require \"test\"\n                test.go()\n            ';\n            echo hello;\n        }\n        location = /foo {\n            echo foo;\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nlocal exec = os.execute\n\nfunction go()\n    local ok, err = pcall(ngx.req.set_uri, \"/foo\", true)\n    if not ok then\n        ngx.log(ngx.ERR, \"error: \", err)\n    end\n\n    exec(\"sleep 5\")\nend\n--- request\nGET /test\n--- response_body\nfoo\n--- no_error_log\n[error]\n--- timeout: 2\n\n\n\n=== TEST 29: abort does not affect following coroutines\n--- config\n        location = /test {\n            rewrite_by_lua 'ngx.exit(0)';\n            content_by_lua '\n                pcall(ngx.say, \"hello world\")\n            ';\n        }\n--- request\nGET /test\n--- response_body\nhello world\n--- no_error_log\n[error]\n--- timeout: 2\n\n\n\n=== TEST 30: ngx.exit(400) should break xpcall and skip os.execute (all in user module)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                test.go()\n            ';\n        }\n--- user_files\n>>> test.lua\nmodule('test', package.seeall)\n\nlocal exec = os.execute\n\nfunction myexit()\n    ngx.exit(400)\nend\n\nfunction go()\n    xpcall(myexit, function () end)\n    exec(\"sleep 5\")\nend\n--- request\nGET /test\n--- response_body_like: 400 Bad Request\n--- error_code: 400\n--- no_error_log\n[error]\n--- timeout: 2\n\n\n\n=== TEST 31: ngx.exec() should skip os.execute (all in user module)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                test.go()\n            ';\n        }\n        location = /foo {\n            echo foo;\n        }\n--- user_files\n>>> test.lua\nlocal os_exec = os.execute\nlocal ngx_exec = ngx.exec\nmodule('test')\n\nfunction go()\n    ngx_exec(\"/foo\")\n    os_exec(\"sleep 5\")\nend\n--- request\nGET /test\n--- response_body\nfoo\n--- no_error_log\n[error]\n--- timeout: 2\n\n\n\n=== TEST 32: ngx.exec() should break pcall and skip os.execute (all in user module)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local test = require \"test\"\n                test.go()\n            ';\n        }\n        location = /foo {\n            echo foo;\n        }\n--- user_files\n>>> test.lua\nlocal os_exec = os.execute\nlocal ngx_exec = ngx.exec\nlocal pcall = pcall\nmodule('test')\n\nfunction go()\n    pcall(ngx_exec, \"/foo\")\n    os_exec(\"sleep 5\")\nend\n--- request\nGET /test\n--- response_body\nfoo\n--- no_error_log\n[error]\n--- timeout: 2\n"
  },
  {
    "path": "t/064-pcall.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\nworker_connections(1014);\n#master_on();\n#workers(4);\n#log_level('warn');\nno_root_location();\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\nour $HtmlDir = html_dir;\n\n#$ENV{LUA_CPATH} = \"/usr/local/openresty/lualib/?.so;\" . $ENV{LUA_CPATH};\n\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: pcall works\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local function f(a, b)\n                    if a == 0 and b == 0 then\n                        error(\"zero error\")\n                    end\n\n                    return 23, \"hello\", true\n                end\n\n                local res = {pcall(f, 0, 0)}\n                ngx.say(\"res len: \", #res)\n                ngx.say(\"res: \", unpack(res))\n\n                res = {pcall(f, 0)}\n                ngx.say(\"res len: \", #res)\n                ngx.say(\"res: \", unpack(res))\n            ';\n        }\n--- request\nGET /test\n--- response_body eval\nqr/^res len: 2\nres: falsecontent_by_lua\\(nginx\\.conf:\\d+\\):4: zero error\nres len: 4\nres: true23hellotrue\n$/s\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: xpcall works\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n        location = /test {\n            content_by_lua '\n                local function f(a, b)\n                    if a == 0 and b == 0 then\n                        error(\"zero error\")\n                    end\n\n                    return 23, \"hello\", true\n                end\n\n                local function g()\n                    return f(0, 0)\n                end\n\n                local function h()\n                    return f(0)\n                end\n\n                local function err(...)\n                    ngx.say(\"error handler called: \", ...)\n                    return \"this is the new err\"\n                end\n\n                local res = {xpcall(g, err)}\n                ngx.say(\"res len: \", #res)\n                ngx.say(\"res: \", unpack(res))\n\n                res = {xpcall(h, err)}\n                ngx.say(\"res len: \", #res)\n                ngx.say(\"res: \", unpack(res))\n            ';\n        }\n--- request\nGET /test\n--- response_body eval\nqr/^error handler called: content_by_lua\\(nginx\\.conf:\\d+\\):4: zero error\nres len: 2\nres: falsethis is the new err\nres len: 4\nres: true23hellotrue\n$/\n\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/065-tcp-socket-timeout.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nBEGIN {\n    if (!defined $ENV{LD_PRELOAD}) {\n        $ENV{LD_PRELOAD} = '';\n    }\n\n    if ($ENV{LD_PRELOAD} !~ /\\bmockeagain\\.so\\b/) {\n        $ENV{LD_PRELOAD} = \"mockeagain.so $ENV{LD_PRELOAD}\";\n    }\n\n    if ($ENV{MOCKEAGAIN} eq 'r') {\n        $ENV{MOCKEAGAIN} = 'rw';\n\n    } else {\n        $ENV{MOCKEAGAIN} = 'w';\n    }\n\n    delete($ENV{TEST_NGINX_USE_HTTP2});\n    $ENV{TEST_NGINX_EVENT_TYPE} = 'poll';\n    $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'get helloworld';\n}\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4 + 6);\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\nlog_level(\"debug\");\nno_long_string();\n#no_diff();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: lua_socket_connect_timeout only\n--- config\n    server_tokens off;\n    lua_socket_connect_timeout 100ms;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nfailed to connect: timeout\n--- error_log\nlua tcp socket connect timeout: 100\nlua tcp socket connect timed out, upstream: 127.0.0.2:12345(127.0.0.2)\n--- timeout: 10\n\n\n\n=== TEST 2: sock:settimeout() overrides lua_socket_connect_timeout\n--- config\n    server_tokens off;\n    lua_socket_connect_timeout 60s;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(150)\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nfailed to connect: timeout\n--- error_log\nlua tcp socket connect timeout: 150\nlua tcp socket connect timed out, upstream: 127.0.0.2:12345(127.0.0.2)\n--- timeout: 10\n\n\n\n=== TEST 3: sock:settimeout(nil) does not override lua_socket_connect_timeout\n--- config\n    server_tokens off;\n    lua_socket_connect_timeout 102ms;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(nil)\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nfailed to connect: timeout\n--- error_log\nlua tcp socket connect timeout: 102\nlua tcp socket connect timed out, upstream: 127.0.0.2:12345(127.0.0.2)\n\n\n\n=== TEST 4: sock:settimeout(0) does not override lua_socket_connect_timeout\n--- config\n    server_tokens off;\n    lua_socket_connect_timeout 102ms;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(0)\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nfailed to connect: timeout\n--- error_log\nlua tcp socket connect timeout: 102\nlua tcp socket connect timed out, upstream: 127.0.0.2:12345(127.0.0.2)\n--- timeout: 10\n\n\n\n=== TEST 5: -1 is bad timeout value\n--- config\n    server_tokens off;\n    lua_socket_connect_timeout 102ms;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(-1)\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        ';\n    }\n--- request\nGET /t\n--- response_body_like chomp\n500 Internal Server Error\n--- error_log\nbad timeout value\n--- error_code: 500\n\n\n\n=== TEST 6: lua_socket_read_timeout only\n--- config\n    server_tokens off;\n    lua_socket_read_timeout 100ms;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local line\n            line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: timeout\n--- error_log\nlua tcp socket read timeout: 100\nlua tcp socket connect timeout: 60000\nlua tcp socket read timed out\n\n\n\n=== TEST 7: sock:settimeout() overrides lua_socket_read_timeout\n--- config\n    server_tokens off;\n    lua_socket_read_timeout 60s;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(150)\n\n            local line\n            line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: timeout\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket read timeout: 150\nlua tcp socket read timed out\n\n\n\n=== TEST 8: sock:settimeout(nil) does not override lua_socket_read_timeout\n--- config\n    server_tokens off;\n    lua_socket_read_timeout 102ms;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(nil)\n\n            local line\n            line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: timeout\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket read timeout: 102\nlua tcp socket read timed out\n\n\n\n=== TEST 9: sock:settimeout(0) does not override lua_socket_read_timeout\n--- config\n    server_tokens off;\n    lua_socket_read_timeout 102ms;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(0)\n\n            local line\n            line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: timeout\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket read timeout: 102\nlua tcp socket read timed out\n\n\n\n=== TEST 10: -1 is bad timeout value\n--- config\n    server_tokens off;\n    lua_socket_read_timeout 102ms;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            sock:settimeout(-1)\n\n            local line\n            line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body_like chomp\n500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad timeout value\n\n\n\n=== TEST 11: lua_socket_send_timeout only\n--- config\n    server_tokens off;\n    lua_socket_send_timeout 100ms;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local bytes\n            bytes, err = sock:send(\"get helloworld!\")\n            if bytes then\n                ngx.say(\"sent: \", bytes)\n            else\n                ngx.say(\"failed to send: \", err)\n            end\n        ';\n    }\n--- request\nGET /t\n--- stap2\nglobal active = 0\nF(ngx_http_lua_socket_send) {\n    active = 1\n    println(probefunc())\n}\nprobe syscall.send,\n    syscall.sendto,\n    syscall.writev\n{\n    if (active && pid() == target()) {\n        println(probefunc())\n    }\n}\n--- response_body\nconnected: 1\nfailed to send: timeout\n--- error_log\nlua tcp socket send timeout: 100\nlua tcp socket connect timeout: 60000\nlua tcp socket write timed out\n\n\n\n=== TEST 12: sock:settimeout() overrides lua_socket_send_timeout\n--- config\n    server_tokens off;\n    lua_socket_send_timeout 60s;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(150)\n\n            local bytes\n            bytes, err = sock:send(\"get helloworld!\")\n            if bytes then\n                ngx.say(\"sent: \", bytes)\n            else\n                ngx.say(\"failed to send: \", err)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to send: timeout\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket send timeout: 150\nlua tcp socket write timed out\n\n\n\n=== TEST 13: sock:settimeout(nil) does not override lua_socket_send_timeout\n--- config\n    server_tokens off;\n    lua_socket_send_timeout 102ms;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(nil)\n\n            local bytes\n            bytes, err = sock:send(\"get helloworld!\")\n            if bytes then\n                ngx.say(\"sent: \", bytes)\n            else\n                ngx.say(\"failed to send: \", err)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to send: timeout\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket send timeout: 102\nlua tcp socket write timed out\n\n\n\n=== TEST 14: sock:settimeout(0) does not override lua_socket_send_timeout\n--- config\n    server_tokens off;\n    lua_socket_send_timeout 102ms;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(0)\n\n            local bytes\n            bytes, err = sock:send(\"get helloworld!\")\n            if bytes then\n                ngx.say(\"sent: \", bytes)\n            else\n                ngx.say(\"failed to send: \", err)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to send: timeout\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket send timeout: 102\nlua tcp socket write timed out\n\n\n\n=== TEST 15: sock:settimeout(-1) does not override lua_socket_send_timeout\n--- config\n    server_tokens off;\n    lua_socket_send_timeout 102ms;\n    #resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            sock:settimeout(-1)\n\n            local bytes\n            bytes, err = sock:send(\"get helloworld!\")\n            if bytes then\n                ngx.say(\"sent: \", bytes)\n            else\n                ngx.say(\"failed to send: \", err)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body_like chomp\n500 Internal Server Error\n--- error_log\nbad timeout value\n--- error_code: 500\n\n\n\n=== TEST 16: exit in user thread (entry thread is still pending on tcpsock:send)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            sock:settimeout(12000)\n\n            local bytes, ok = sock:send(\"get helloworld!\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua tcp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: re-connect after timed out\n--- config\n    server_tokens off;\n    lua_socket_connect_timeout 100ms;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"1: failed to connect: \", err)\n\n                local ok, err = sock:connect(\"127.0.0.1\", ngx.var.server_port)\n                if not ok then\n                    ngx.say(\"2: failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"2: connected: \", ok)\n                return\n            end\n\n            ngx.say(\"1: connected: \", ok)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n1: failed to connect: timeout\n2: connected: 1\n--- error_log\nlua tcp socket connect timeout: 100\nlua tcp socket connect timed out, upstream: 127.0.0.2:12345(127.0.0.2)\n--- timeout: 10\n\n\n\n=== TEST 18: re-send on the same object after a send timeout happens\n--- config\n    server_tokens off;\n    lua_socket_send_timeout 100ms;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local bytes\n            bytes, err = sock:send(\"get helloworld!\")\n            if bytes then\n                ngx.say(\"sent: \", bytes)\n            else\n                ngx.say(\"failed to send: \", err)\n                bytes, err = sock:send(\"blah\")\n                if not bytes then\n                    ngx.say(\"failed to send again: \", err)\n                end\n            end\n        ';\n    }\n--- request\nGET /t\n--- stap2\nglobal active = 0\nF(ngx_http_lua_socket_send) {\n    active = 1\n    println(probefunc())\n}\nprobe syscall.send,\n    syscall.sendto,\n    syscall.writev\n{\n    if (active && pid() == target()) {\n        println(probefunc())\n    }\n}\n--- response_body\nconnected: 1\nfailed to send: timeout\nfailed to send again: closed\n--- error_log\nlua tcp socket send timeout: 100\nlua tcp socket connect timeout: 60000\nlua tcp socket write timed out\n\n\n\n=== TEST 19: abort when upstream sockets pending on writes\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(100)\n            ngx.thread.spawn(function () ngx.sleep(0.001) ngx.say(\"done\") ngx.exit(200) end)\n            local bytes\n            bytes, err = sock:send(\"get helloworld!\")\n            if bytes then\n                ngx.say(\"sent: \", bytes)\n            else\n                ngx.say(\"failed to send: \", err)\n            end\n        ';\n    }\n--- request\nGET /t\n--- stap2\nglobal active = 0\nF(ngx_http_lua_socket_send) {\n    active = 1\n    println(probefunc())\n}\nprobe syscall.send,\n    syscall.sendto,\n    syscall.writev\n{\n    if (active && pid() == target()) {\n        println(probefunc())\n    }\n}\n--- response_body\nconnected: 1\ndone\n--- error_log\nlua tcp socket send timeout: 100\nlua tcp socket connect timeout: 60000\n--- no_error_log\nlua tcp socket write timed out\n\n\n\n=== TEST 20: abort when downstream socket pending on writes\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            ngx.send_headers()\n            ngx.flush(true)\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.say(\"failed to acquire the req socket: \", err)\n                return\n            end\n\n            sock:settimeout(100)\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.001)\n                ngx.log(ngx.WARN, \"quitting request now\")\n                ngx.exit(200)\n            end)\n            local bytes\n            bytes, err = sock:send(\"e\\\\r\\\\nget helloworld!\")\n            if bytes then\n                ngx.say(\"sent: \", bytes)\n            else\n                ngx.say(\"failed to send: \", err)\n            end\n        ';\n    }\n--- request\nGET /t\n--- stap2\nglobal active = 0\nF(ngx_http_lua_socket_send) {\n    active = 1\n    println(probefunc())\n}\nprobe syscall.send,\n    syscall.sendto,\n    syscall.writev\n{\n    if (active && pid() == target()) {\n        println(probefunc())\n    }\n}\n--- ignore_response\n--- error_log\nlua tcp socket send timeout: 100\nquitting request now\n--- no_error_log\nlua tcp socket write timed out\n[alert]\n--- skip_eval: 4: $ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 21: read timeout on receive(N)\n--- config\n    server_tokens off;\n    lua_socket_read_timeout 100ms;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            sock:settimeout(10)\n\n            local line\n            line, err = sock:receive(3)\n            if line then\n                ngx.say(\"received: \", line)\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: timeout\n--- error_log\nlua tcp socket read timeout: 10\nlua tcp socket connect timeout: 60000\nlua tcp socket read timed out\n\n\n\n=== TEST 22: concurrent operations while writing\n--- config\n    server_tokens off;\n    lua_socket_log_errors off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ready = false\n\n            local function f()\n                while not ready do\n                    ngx.sleep(0.001)\n                end\n\n                local bytes, err = sock:send(\"flush_all\")\n                ngx.say(\"send: \", bytes, \" \", err)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n\n                local ok, err = sock:getreusedtimes()\n                ngx.say(\"getreusedtimes: \", ok, \" \", err)\n\n                local ok, err = sock:setkeepalive()\n                ngx.say(\"setkeepalive: \", ok, \" \", err)\n\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n                ngx.say(\"connect: \", ok, \" \", err)\n\n                sock:settimeout(1)\n                local res, err = sock:receive(1)\n                ngx.say(\"receive: \", res, \" \", err)\n            end\n\n            local ok, err = ngx.thread.spawn(f)\n            if not ok then\n                ngx.say(\"failed to spawn writer thread: \", err)\n                return\n            end\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            ngx.say(\"connect: \", ok, \" \", err)\n\n            ready = true\n\n            sock:settimeout(300)\n            local bytes, err = sock:send(\"get helloworld!\")\n            if not bytes then\n                ngx.say(\"send failed: \", err)\n            end\n\n            local ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnect: 1 nil\nsend: nil socket busy writing\nclose: nil socket busy writing\ngetreusedtimes: 0 nil\nsetkeepalive: nil socket busy writing\nconnect: nil socket busy writing\nreceive: nil timeout\nsend failed: timeout\nclose: 1 nil\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: timeout overflow detection\n--- config\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local ok, err = pcall(sock.settimeout, sock, (2 ^ 31) - 1)\n            if not ok then\n                ngx.say(\"failed to set timeout: \", err)\n            else\n                ngx.say(\"settimeout: ok\")\n            end\n\n            ok, err = pcall(sock.settimeout, sock, 2 ^ 31)\n            if not ok then\n                ngx.say(\"failed to set timeout: \", err)\n            else\n                ngx.say(\"settimeout: ok\")\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body_like\nsettimeout: ok\nfailed to set timeout: bad timeout value\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/066-socket-receiveuntil.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\nno_long_string();\n#no_diff();\n#log_level 'warn';\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: memcached read lines\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"flush_all\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local readline = sock:receiveuntil(\"\\\\r\\\\n\")\n            local line, err, part = readline()\n            if line then\n                ngx.say(\"received: \", line)\n\n            else\n                ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 11\nreceived: OK\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: http read lines\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local readline = sock:receiveuntil(\"\\\\r\\\\n\")\n            local line, err, part\n\n            for i = 1, 7 do\n                line, err, part = readline()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: HTTP/1.1 200 OK\nread: Server: nginx\nread: Content-Type: text/plain\nread: Content-Length: 4\nread: Connection: close\nread: \nfailed to read a line: closed [foo\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: http read all the headers in a single run\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local line, err, part\n\n            for i = 1, 2 do\n                line, err, part = read_headers()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: HTTP/1.1 200 OK\\r\nServer: nginx\\r\nContent-Type: text/plain\\r\nContent-Length: 4\\r\nConnection: close\nfailed to read a line: closed [foo\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: ambiguous boundary patterns (abcabd)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"abcabd\")\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"abcabcabd\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: abc\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: ambiguous boundary patterns (aa)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"aa\")\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        content_by_lua 'ngx.say(\"abcabcaad\")';\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: abcabc\nfailed to read a line: closed [d\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: ambiguous boundary patterns (aaa)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"aaa\")\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo abaabcaaaef;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: abaabc\nfailed to read a line: closed [ef\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: ambiguous boundary patterns (aaaaad)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"aaaaad\")\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo baaaaaaaaeaaaaaaadf;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: baaaaaaaaeaa\nfailed to read a line: closed [f\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: ambiguous boundary patterns (aaaaad), small buffer, 2 bytes\n--- no_http2\n--- config\n    server_tokens off;\n    lua_socket_buffer_size 2;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"aaaaad\")\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo baaaaaaaaeaaaaaaadf;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: baaaaaaaaeaa\nfailed to read a line: closed [f\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: ambiguous boundary patterns (aaaaad), small buffer, 1 byte\n--- no_http2\n--- config\n    server_tokens off;\n    lua_socket_buffer_size 1;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"aaaaad\")\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo baaaaaaaaeaaaaaaadf;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: baaaaaaaaeaa\nfailed to read a line: closed [f\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: ambiguous boundary patterns (abcabdabcabe)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"abcabdabcabe\")\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo abcabdabcabdabcabe;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: abcabd\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: ambiguous boundary patterns (abcabdabcabe 2)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"abcabdabcabe\")\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo abcabdabcabcabdabcabe;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: abcabdabc\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: ambiguous boundary patterns (abcabdabcabe 3)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"abcabdabcabe\")\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo abcabcabdabcabe;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: abc\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: ambiguous boundary patterns (abcabdabcabe 4)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"abcabdabcabe\")\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo ababcabdabcabe;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: ab\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: ambiguous boundary patterns (--abc)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"--abc\")\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo -- ----abc;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: --\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: ambiguous boundary patterns (--abc)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"--abc\")\n\n            for i = 1, 6 do\n                local line, err, part = reader(4)\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo \"hello, world ----abc\";\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: hell\nread: o, w\nread: orld\nread:  --\nfailed to read a line: nil [nil]\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: ambiguous boundary patterns (--abc), small buffer\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        lua_socket_buffer_size 1;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"--abc\")\n\n            for i = 1, 6 do\n                local line, err, part = reader(4)\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo \"hello, world ----abc\";\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: hell\nread: o, w\nread: orld\nread:  --\nfailed to read a line: nil [nil]\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: ambiguous boundary patterns (--abc), small buffer, mixed by other reading calls\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        lua_socket_buffer_size 1;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"--abc\")\n\n            for i = 1, 7 do\n                local line, err, part = reader(4)\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a chunk: \", err, \" [\", part, \"]\")\n                end\n\n                local data, err, part = sock:receive(1)\n                if not data then\n                    ngx.say(\"failed to read a byte: \", err, \" [\", part, \"]\")\n                    break\n                else\n                    ngx.say(\"read one byte: \", data)\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo \"hello, world ----abc\";\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: hell\nread one byte: o\nread: , wo\nread one byte: r\nread: ld -\nread one byte: -\nread: \nread one byte: \n\nfailed to read a chunk: nil [nil]\nfailed to read a byte: closed []\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: ambiguous boundary patterns (abcabd), small buffer\n--- no_http2\n--- config\n    server_tokens off;\n    lua_socket_buffer_size 3;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"abcabd\")\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo abcabcabd;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: abc\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: long patterns\nthis exposed a memory leak in receiveuntil\n--- config\n    location /t {\n        content_by_lua '\n            local sock, err = ngx.req.socket()\n            if not sock then\n                ngx.say(\"failed to get req socket: \", err)\n                return\n            end\n            local reader, err = sock:receiveuntil(\"------------------------------------------- abcdefghijklmnopqrstuvwxyz\")\n            if not reader then\n                ngx.say(\"failed to get reader: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\n    POST /t\n\n--- more_headers: Content-Length: 1024\n--- response_body\nok\n--- no_error_log\n[error]\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 20: add pending bytes\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        lua_socket_buffer_size 1;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"--abc\")\n\n            for i = 1, 4 do\n                local line, err, part = reader(2)\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo -- -----abc;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: --\nread: -\nfailed to read a line: nil [nil]\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: ambiguous boundary patterns (--abc), mixed by other reading calls consume boundary\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\r\\n\\r\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"--abc\")\n\n            for i = 1, 5 do\n                local line, err, part = reader(2)\n                if not line then\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                    break\n\n                else\n                    ngx.say(\"read: \", line)\n                end\n\n                local data, err, part = sock:receive(1)\n                if not data then\n                    ngx.say(\"failed to read a byte: \", err, \" [\", part, \"]\")\n                    break\n\n                else\n                    ngx.say(\"read one byte: \", data)\n                end\n            end\n\n            local line, err, part = reader(2)\n            if not line then\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read: \", line)\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n\n    location /foo {\n        echo -- ----abc----abc-;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: --\nread one byte: -\nread: -a\nread one byte: b\nread: c-\nread one byte: -\nread: \nread one byte: -\nfailed to read a line: nil [nil]\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: ambiguous boundary patterns (--abc), mixed by other reading calls (including receiveuntil) consume boundary\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\r\\n\\r\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader1 = sock:receiveuntil(\"--abc\")\n            local reader2 = sock:receiveuntil(\"-ab\")\n\n            local line, err, part = reader1(2)\n            if not line then\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read: \", line)\n            end\n\n            local data, err, part = sock:receive(1)\n            if not data then\n                ngx.say(\"failed to read a byte: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read one byte: \", data)\n            end\n\n            local line, err, part = reader1(1)\n            if not line then\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read: \", line)\n            end\n\n            local line, err, part = reader2(2)\n            if not line then\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read: \", line)\n            end\n\n            local line, err, part = reader1()\n            if not line then\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read: \", line)\n            end\n\n            local line, err, part = reader1()\n            if not line then\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read: \", line)\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n\n    location /foo {\n        echo -- ------abd----abc;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: --\nread one byte: -\nread: -\nread: -\nread: d--\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: ambiguous boundary patterns (--abc), mixed by other reading calls consume boundary, small buffer\n--- config\n    lua_socket_buffer_size 3;\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\r\\n\\r\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"--abc\")\n\n            for i = 1, 5 do\n                local line, err, part = reader(2)\n                if not line then\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                    break\n\n                else\n                    ngx.say(\"read: \", line)\n                end\n\n                local data, err, part = sock:receive(1)\n                if not data then\n                    ngx.say(\"failed to read a byte: \", err, \" [\", part, \"]\")\n                    break\n\n                else\n                    ngx.say(\"read one byte: \", data)\n                end\n            end\n\n            local line, err, part = reader(2)\n            if not line then\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read: \", line)\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n\n    location /foo {\n        echo -- ----abc----abc-;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: --\nread one byte: -\nread: -a\nread one byte: b\nread: c-\nread one byte: -\nread: \nread one byte: -\nfailed to read a line: nil [nil]\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 24: ambiguous boundary patterns (--abc), mixed by other reading calls (including receiveuntil) consume boundary, small buffer\n--- config\n    lua_socket_buffer_size 3;\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\r\\n\\r\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader1 = sock:receiveuntil(\"--abc\")\n            local reader2 = sock:receiveuntil(\"-ab\")\n\n            local line, err, part = reader1(2)\n            if not line then\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read: \", line)\n            end\n\n            local data, err, part = sock:receive(1)\n            if not data then\n                ngx.say(\"failed to read a byte: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read one byte: \", data)\n            end\n\n            local line, err, part = reader1(1)\n            if not line then\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read: \", line)\n            end\n\n            local line, err, part = reader2(2)\n            if not line then\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read: \", line)\n            end\n\n            local line, err, part = reader1()\n            if not line then\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read: \", line)\n            end\n\n            local line, err, part = reader1()\n            if not line then\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read: \", line)\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n\n    location /foo {\n        echo -- ------abd----abc;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: --\nread one byte: -\nread: -\nread: -\nread: d--\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 25: ambiguous boundary patterns (ab1ab2), ends half way\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\r\\n\\r\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            if true then\n                local reader = sock:receiveuntil(\"ab1ab2\")\n\n                local line, err, part = reader(2)\n                if not line then\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n                else\n                    ngx.say(\"read: \", line)\n                end\n            end\n\n            collectgarbage(\"collect\")\n\n            local data, err, part = sock:receive(3)\n            if not data then\n                ngx.say(\"failed to read three bytes: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read three bytes: \", data)\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n\n    location /foo {\n        echo -- ab1ab1;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: ab1\nread three bytes: ab1\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 26: ambiguous boundary patterns (ab1ab2), ends half way, small buffer\n--- config\n    lua_socket_buffer_size 3;\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\r\\n\\r\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            if true then\n                local reader = sock:receiveuntil(\"ab1ab2\")\n\n                local line, err, part = reader(2)\n                if not line then\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n\n                else\n                    ngx.say(\"read: \", line)\n                end\n            end\n\n            collectgarbage(\"collect\")\n\n            local data, err, part = sock:receive(3)\n            if not data then\n                ngx.say(\"failed to read three bytes: \", err, \" [\", part, \"]\")\n\n            else\n                ngx.say(\"read three bytes: \", data)\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n\n    location /foo {\n        echo -- ab1ab1;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: ab1\nread three bytes: ab1\nclose: 1 nil\n}\n--- no_error_log\n[error]\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n"
  },
  {
    "path": "t/067-req-socket.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_USE_HTTP3}) {\n        $SkipReason = \"http3 does not support ngx.req.socket\";\n    } elsif ($ENV{TEST_NGINX_USE_HTTP2}) {\n        $SkipReason = \"http2 does not support ngx.req.socket\";\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 9);\n\nour $HtmlDir = html_dir;\n\n#$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\nno_long_string();\n#no_diff();\n#log_level 'warn';\nno_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /t {\n        content_by_lua '\n            local sock, err = ngx.req.socket()\n            if sock then\n                ngx.say(\"got the request socket\")\n            else\n                ngx.say(\"failed to get the request socket: \", err)\n            end\n\n            for i = 1, 3 do\n                local data, err, part = sock:receive(5)\n                if data then\n                    ngx.say(\"received: \", data)\n                else\n                    ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n                end\n            end\n        ';\n    }\n--- request\nPOST /t\nhello world\n--- response_body\ngot the request socket\nreceived: hello\nreceived:  worl\nfailed to receive: closed [d]\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: multipart rfc sample (just partial streaming)\n--- config\n    location /t {\n        content_by_lua '\n            local sock, err = ngx.req.socket()\n            if sock then\n                ngx.say(\"got the request socket\")\n            else\n                ngx.say(\"failed to get the request socket: \", err)\n            end\n\n            local boundary\n            local header = ngx.var.http_content_type\n            local m = ngx.re.match(header, [[; +boundary=(?:\"(.*?)\"|(\\\\w+))]], \"jo\")\n            if m then\n                boundary = m[1] or m[2]\n\n            else\n                ngx.say(\"invalid content-type header\")\n                return\n            end\n\n            local read_to_boundary = sock:receiveuntil(\"\\\\r\\\\n--\" .. boundary)\n            local read_line = sock:receiveuntil(\"\\\\r\\\\n\")\n\n            local data, err, part = read_to_boundary()\n            if data then\n                ngx.say(\"preamble: [\" .. data .. \"]\")\n            else\n                ngx.say(\"failed to read the first boundary: \", err)\n                return\n            end\n\n            local i = 1\n            while true do\n                local line, err = read_line()\n\n                if not line then\n                    ngx.say(\"failed to read post-boundary line: \", err)\n                    return\n                end\n\n                m = ngx.re.match(line, \"--$\", \"jo\")\n                if m then\n                    ngx.say(\"found the end of the stream\")\n                    return\n                end\n\n                while true do\n                    local line, err = read_line()\n                    if not line then\n                        ngx.say(\"failed to read part \", i, \" header: \", err)\n                        return\n                    end\n\n                    if line == \"\" then\n                        -- the header part completes\n                        break\n                    end\n\n                    ngx.say(\"part \", i, \" header: [\", line, \"]\")\n                end\n\n                local data, err, part = read_to_boundary()\n                if data then\n                    ngx.say(\"part \", i, \" body: [\" .. data .. \"]\")\n                else\n                    ngx.say(\"failed to read part \", i + 1, \" boundary: \", err)\n                    return\n                end\n\n                i = i + 1\n            end\n        ';\n    }\n--- request eval\n\"POST /t\nThis is the preamble.  It is to be ignored, though it\nis a handy place for mail composers to include an\nexplanatory note to non-MIME compliant readers.\\r\n--simple boundary\\r\n\\r\nThis is implicitly typed plain ASCII text.\nIt does NOT end with a linebreak.\\r\n--simple boundary\\r\nContent-type: text/plain; charset=us-ascii\\r\n\\r\nThis is explicitly typed plain ASCII text.\nIt DOES end with a linebreak.\n\\r\n--simple boundary--\\r\nThis is the epilogue.  It is also to be ignored.\n\"\n--- more_headers\nContent-Type: multipart/mixed; boundary=\"simple boundary\"\n--- response_body\ngot the request socket\npreamble: [This is the preamble.  It is to be ignored, though it\nis a handy place for mail composers to include an\nexplanatory note to non-MIME compliant readers.]\npart 1 body: [This is implicitly typed plain ASCII text.\nIt does NOT end with a linebreak.]\npart 2 header: [Content-type: text/plain; charset=us-ascii]\npart 2 body: [This is explicitly typed plain ASCII text.\nIt DOES end with a linebreak.\n]\nfound the end of the stream\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: multipart rfc sample (completely streaming)\n--- config\n    location /t {\n        content_by_lua '\n            local sock, err = ngx.req.socket()\n            if sock then\n                ngx.say(\"got the request socket\")\n            else\n                ngx.say(\"failed to get the request socket: \", err)\n            end\n\n            local boundary\n            local header = ngx.var.http_content_type\n            local m = ngx.re.match(header, [[; +boundary=(?:\"(.*?)\"|(\\\\w+))]], \"jo\")\n            if m then\n                boundary = m[1] or m[2]\n\n            else\n                ngx.say(\"invalid content-type header\")\n                return\n            end\n\n            local read_to_boundary = sock:receiveuntil(\"\\\\r\\\\n--\" .. boundary)\n            local read_line = sock:receiveuntil(\"\\\\r\\\\n\")\n\n            local preamble = \"\"\n            while true do\n                local data, err, part = read_to_boundary(1)\n                if data then\n                    preamble = preamble .. data\n\n                elseif not err then\n                    break\n\n                else\n                    ngx.say(\"failed to read the first boundary: \", err)\n                    return\n                end\n            end\n\n            ngx.say(\"preamble: [\" .. preamble .. \"]\")\n\n            local i = 1\n            while true do\n                local line, err = read_line(50)\n\n                if not line and err then\n                    ngx.say(\"1: failed to read post-boundary line: \", err)\n                    return\n                end\n\n                if line then\n                    local dummy\n                    dummy, err = read_line(1)\n                    if err then\n                        ngx.say(\"2: failed to read post-boundary line: \", err)\n                        return\n                    end\n\n                    if dummy then\n                        ngx.say(\"bad post-boundary line: \", dummy)\n                        return\n                    end\n\n                    m = ngx.re.match(line, \"--$\", \"jo\")\n                    if m then\n                        ngx.say(\"found the end of the stream\")\n                        return\n                    end\n                end\n\n                while true do\n                    local line, err = read_line(50)\n                    if not line and err then\n                        ngx.say(\"failed to read part \", i, \" header: \", err)\n                        return\n                    end\n\n                    if line then\n                        local line, err = read_line(1)\n                        if line or err then\n                            ngx.say(\"error\")\n                            return\n                        end\n                    end\n\n                    if line == \"\" then\n                        -- the header part completes\n                        break\n                    end\n\n                    ngx.say(\"part \", i, \" header: [\", line, \"]\")\n                end\n\n                local body = \"\"\n\n                while true do\n                    local data, err, part = read_to_boundary(1)\n                    if data then\n                        body = body .. data\n\n                    elseif err then\n                        ngx.say(\"failed to read part \", i + 1, \" boundary: \", err)\n                        return\n\n                    else\n                        break\n                    end\n                end\n\n                ngx.say(\"part \", i, \" body: [\" .. body .. \"]\")\n\n                i = i + 1\n            end\n        ';\n    }\n--- request eval\n\"POST /t\nThis is the preamble.  It is to be ignored, though it\nis a handy place for mail composers to include an\nexplanatory note to non-MIME compliant readers.\\r\n--simple boundary\\r\n\\r\nThis is implicitly typed plain ASCII text.\nIt does NOT end with a linebreak.\\r\n--simple boundary\\r\nContent-type: text/plain; charset=us-ascii\\r\n\\r\nThis is explicitly typed plain ASCII text.\nIt DOES end with a linebreak.\n\\r\n--simple boundary--\\r\nThis is the epilogue.  It is also to be ignored.\n\"\n--- more_headers\nContent-Type: multipart/mixed; boundary=\"simple boundary\"\n--- response_body\ngot the request socket\npreamble: [This is the preamble.  It is to be ignored, though it\nis a handy place for mail composers to include an\nexplanatory note to non-MIME compliant readers.]\npart 1 body: [This is implicitly typed plain ASCII text.\nIt does NOT end with a linebreak.]\npart 2 header: [Content-type: text/plain; charset=us-ascii]\npart 2 body: [This is explicitly typed plain ASCII text.\nIt DOES end with a linebreak.\n]\nfound the end of the stream\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: attempt to use the req socket across request boundary\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        content_by_lua '\n            local test = require \"test\"\n            test.go()\n            ngx.say(\"done\")\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock, err\n\nfunction go()\n    if not sock then\n        sock, err = ngx.req.socket()\n        if sock then\n            ngx.say(\"got the request socket\")\n        else\n            ngx.say(\"failed to get the request socket: \", err)\n        end\n    else\n        for i = 1, 3 do\n            local data, err, part = sock:receive(5)\n            if data then\n                ngx.say(\"received: \", data)\n            else\n                ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n            end\n        end\n    end\nend\n--- request\nPOST /t\nhello world\n--- response_body_like\n(?:got the request socket\n|failed to receive: closed [d]\n)?done\n--- no_error_log\n[alert]\n\n\n\n=== TEST 5: receive until on request_body - receiveuntil(1) on the last byte of the body\nSee https://groups.google.com/group/openresty/browse_thread/thread/43cf01da3c681aba for details\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        content_by_lua '\n            local test = require \"test\"\n            test.go()\n            ngx.say(\"done\")\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go()\n   local sock, err = ngx.req.socket()\n   if sock then\n      ngx.say(\"got the request socket\")\n   else\n      ngx.say(\"failed to get the request socket: \", err)\n      return\n   end\n\n   local data, err, part = sock:receive(56)\n   if data then\n      ngx.say(\"received: \", data)\n   else\n      ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n   end\n\n   local discard_line = sock:receiveuntil('\\r\\n')\n\n   local data, err, part = discard_line(8192)\n   if data then\n      ngx.say(\"received len: \", #data)\n   else\n      ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n   end\n\n   local data, err, part = discard_line(1)\n   if data then\n      ngx.say(\"received: \", data)\n   else\n      ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n   end\nend\n--- request\nPOST /t\n-----------------------------820127721219505131303151179################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################$\n--- response_body\ngot the request socket\nreceived: -----------------------------820127721219505131303151179\nreceived len: 8192\nreceived: $\ndone\n--- no_error_log\n[error]\n--- timeout: 10\n\n\n\n=== TEST 6: pipelined POST requests\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        content_by_lua '\n            local test = require \"test\"\n            test.go()\n            ngx.say(\"done\")\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go()\n   local sock, err = ngx.req.socket()\n   if sock then\n      ngx.say(\"got the request socket\")\n   else\n      ngx.say(\"failed to get the request socket: \", err)\n      return\n   end\n\n   while true do\n       local data, err, part = sock:receive(4)\n       if data then\n          ngx.say(\"received: \", data)\n       else\n          ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n          return\n       end\n   end\nend\n--- pipelined_requests eval\n[\"POST /t\nhello, world\",\n\"POST /t\nhiya, world\"]\n--- response_body eval\n[\"got the request socket\nreceived: hell\nreceived: o, w\nreceived: orld\nfailed to receive: closed []\ndone\n\",\n\"got the request socket\nreceived: hiya\nreceived: , wo\nfailed to receive: closed [rld]\ndone\n\"]\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: Expect & 100 Continue\n--- config\n    location /t {\n        content_by_lua '\n            local sock, err = ngx.req.socket()\n            if sock then\n                ngx.say(\"got the request socket\")\n            else\n                ngx.say(\"failed to get the request socket: \", err)\n                return\n            end\n\n            for i = 1, 3 do\n                local data, err, part = sock:receive(5)\n                if data then\n                    ngx.say(\"received: \", data)\n                else\n                    ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n                end\n            end\n        ';\n    }\n--- request\nPOST /t\nhello world\n--- more_headers\nExpect: 100-Continue\n--- error_code: 100\n--- response_body_like chomp\n\\breceived: hello\\b.*?\\breceived:  worl\\b\n--- no_error_log\n[error]\n--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n\n\n\n=== TEST 8: pipelined requests, big buffer, small steps\n--- config\n    location /t {\n        lua_socket_buffer_size 5;\n        content_by_lua '\n            local sock, err = ngx.req.socket()\n            if sock then\n                ngx.say(\"got the request socket\")\n            else\n                ngx.say(\"failed to get the request socket: \", err)\n            end\n\n            for i = 1, 6 do\n                local data, err, part = sock:receive(2)\n                if data then\n                    ngx.say(\"received: \", data)\n                else\n                    ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n                end\n            end\n        ';\n    }\n--- stap2\nM(http-lua-req-socket-consume-preread) {\n    println(\"preread: \", user_string_n($arg2, $arg3))\n}\n\n--- pipelined_requests eval\n[\"POST /t\nhello world\",\"POST /t\nhiya globe\"]\n--- response_body eval\n[\"got the request socket\nreceived: he\nreceived: ll\nreceived: o \nreceived: wo\nreceived: rl\nfailed to receive: closed [d]\n\",\"got the request socket\nreceived: hi\nreceived: ya\nreceived:  g\nreceived: lo\nreceived: be\nfailed to receive: closed []\n\"]\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: chunked support is still a TODO\n--- config\n    location /t {\n        content_by_lua '\n            local sock, err = ngx.req.socket()\n            if sock then\n                ngx.say(\"got the request socket\")\n            else\n                ngx.req.read_body()\n                ngx.say(\"failed to get the request socket: \", err)\n                return\n            end\n\n            for i = 1, 3 do\n                local data, err, part = sock:receive(5)\n                if data then\n                    ngx.say(\"received: \", data)\n                else\n                    ngx.say(\"failed to receive: \", err, \" [\", part, \"]\")\n                end\n            end\n        ';\n    }\n--- raw_request eval\n\"POST /t HTTP/1.1\\r\nHost: localhost\\r\nTransfer-Encoding: chunked\\r\nConnection: close\\r\n\\r\nb\\r\nhello world\\r\n0\\r\n\\r\n\"\n--- stap2\n/*\nF(ngx_http_finalize_request) {\n    if ($r->main->count == 2) {\n        print_ubacktrace()\n    }\n}\nF(ngx_http_free_request) {\n    print_ubacktrace()\n}\n*/\n--- response_body\nfailed to get the request socket: chunked request bodies not supported yet\n--- no_error_log\n[error]\n[alert]\n--- skip_nginx: 4: <1.3.9\n\n\n\n=== TEST 10: chunked support in ngx.req.read_body\n--- config\n    location /t {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.say(ngx.req.get_body_data())\n        ';\n    }\n--- raw_request eval\n\"POST /t HTTP/1.1\\r\nHost: localhost\\r\nTransfer-Encoding: chunked\\r\nConnection: close\\r\n\\r\nb\\r\nhello world\\r\n0\\r\n\\r\n\"\n--- stap2\n/*\nF(ngx_http_finalize_request) {\n    if ($r->main->count == 2) {\n        print_ubacktrace()\n    }\n}\nF(ngx_http_free_request) {\n    print_ubacktrace()\n}\n*/\n--- response_body\nhello world\n--- no_error_log\n[error]\n[alert]\n--- skip_nginx: 4: <1.3.9\n\n\n\n=== TEST 11: downstream cosocket for GET requests (w/o request bodies)\n--- config\n    #resolver 8.8.8.8;\n    location = /t {\n        content_by_lua '\n           local sock, err = ngx.req.socket()\n\n           if not sock then\n              ngx.say(\"failed to get socket: \", err)\n              return nil\n           end\n\n           while true do\n              local data, err, partial = sock:receive(4096)\n\n              ngx.log(ngx.INFO, \"Received data\")\n\n              if err then\n                 ngx.say(\"err: \", err)\n                 if partial then\n                    ngx.print(partial)\n                 end\n\n                 break\n              end\n\n              if data then\n                 ngx.print(data)\n              end\n           end\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nfailed to get socket: no body\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: downstream cosocket for POST requests with 0 size bodies\n--- config\n    #resolver 8.8.8.8;\n    location = /t {\n        content_by_lua '\n           local sock, err = ngx.req.socket()\n\n           if not sock then\n              ngx.say(\"failed to get socket: \", err)\n              return nil\n           end\n\n           while true do\n              local data, err, partial = sock:receive(4096)\n\n              ngx.log(ngx.INFO, \"Received data\")\n\n              if err then\n                 ngx.say(\"err: \", err)\n                 if partial then\n                    ngx.print(partial)\n                 end\n\n                 break\n              end\n\n              if data then\n                 ngx.print(data)\n              end\n           end\n        ';\n    }\n\n--- request\nPOST /t\n--- more_headers\nContent-Length: 0\n--- response_body\nfailed to get socket: no body\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: failing reread after reading timeout happens\n--- config\n    location = /t {\n        content_by_lua '\n            local sock, err = ngx.req.socket()\n\n            if not sock then\n               ngx.say(\"failed to get socket: \", err)\n               return nil\n            end\n\n            sock:settimeout(100);\n\n            local data, err, partial = sock:receive(4096)\n            if err then\n               ngx.say(\"err: \", err, \", partial: \", partial)\n            end\n\n            local data, err, partial = sock:receive(4096)\n            if err then\n               ngx.say(\"err: \", err, \", partial: \", partial)\n               return\n            end\n        ';\n    }\n\n--- raw_request eval\n\"POST /t HTTP/1.0\\r\nHost: localhost\\r\nContent-Length: 10245\\r\n\\r\nhello\"\n--- response_body\nerr: timeout, partial: hello\nerr: timeout, partial: \n\n--- error_log\nlua tcp socket read timed out\n\n\n\n=== TEST 14: successful reread after reading timeout happens (receive -> receive)\n--- config\n    location = /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", ngx.var.server_port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"POST /back HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nContent-Length: 1024\\\\r\\\\n\\\\r\\\\nabc\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n            else\n                ngx.say(\"sent: \", bytes)\n            end\n\n            ngx.sleep(0.2)\n\n            local bytes, err = sock:send(\"hello world\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n            else\n                ngx.say(\"sent: \", bytes)\n            end\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local header, err = reader()\n            if not header then\n                ngx.say(\"failed to receive header: \", err)\n                return\n            end\n\n            for i = 1, 2 do\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive line: \", err)\n                    return\n                end\n                ngx.say(\"received: \", line)\n            end\n        ';\n    }\n\n    location = /back {\n        content_by_lua '\n            ngx.send_headers()\n            ngx.flush(true)\n\n            local sock, err = ngx.req.socket()\n\n            if not sock then\n               ngx.say(\"failed to get socket: \", err)\n               return nil\n            end\n\n            sock:settimeout(100);\n\n            local data, err, partial = sock:receive(4096)\n            if err then\n               ngx.say(\"err: \", err, \", partial: \", partial)\n            else\n                ngx.say(\"received: \", data)\n            end\n\n            ngx.sleep(0.1)\n\n            local data, err, partial = sock:receive(11)\n            if err then\n               ngx.say(\"err: \", err, \", partial: \", partial)\n            else\n                ngx.say(\"received: \", data)\n            end\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nsent: 65\nsent: 11\nreceived: err: timeout, partial: abc\nreceived: received: hello world\n\n--- error_log\nlua tcp socket read timed out\n\n\n\n=== TEST 15: successful reread after reading timeout happens (receive -> receiveuntil)\n--- config\n    location = /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", ngx.var.server_port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"POST /back HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nContent-Length: 1024\\\\r\\\\n\\\\r\\\\nabc\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n            else\n                ngx.say(\"sent: \", bytes)\n            end\n\n            ngx.sleep(0.2)\n\n            local bytes, err = sock:send(\"hello world\\\\n\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n            else\n                ngx.say(\"sent: \", bytes)\n            end\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local header, err = reader()\n            if not header then\n                ngx.say(\"failed to receive header: \", err)\n                return\n            end\n\n            for i = 1, 2 do\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive line: \", err)\n                    return\n                end\n                ngx.say(\"received: \", line)\n            end\n        ';\n    }\n\n    location = /back {\n        content_by_lua '\n            ngx.send_headers()\n            ngx.flush(true)\n\n            local sock, err = ngx.req.socket()\n\n            if not sock then\n               ngx.say(\"failed to get socket: \", err)\n               return nil\n            end\n\n            sock:settimeout(100);\n\n            local data, err, partial = sock:receive(4096)\n            if err then\n               ngx.say(\"err: \", err, \", partial: \", partial)\n            else\n                ngx.say(\"received: \", data)\n            end\n\n            ngx.sleep(0.1)\n\n            local reader = sock:receiveuntil(\"\\\\n\")\n            local data, err, partial = reader()\n            if err then\n               ngx.say(\"err: \", err, \", partial: \", partial)\n            else\n                ngx.say(\"received: \", data)\n            end\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nsent: 65\nsent: 12\nreceived: err: timeout, partial: abc\nreceived: received: hello world\n\n--- error_log\nlua tcp socket read timed out\n\n\n\n=== TEST 16: successful reread after reading timeout happens (receiveuntil -> receive)\n--- config\n    location = /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", ngx.var.server_port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"POST /back HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nContent-Length: 1024\\\\r\\\\n\\\\r\\\\nabc\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n            else\n                ngx.say(\"sent: \", bytes)\n            end\n\n            ngx.sleep(0.2)\n\n            local bytes, err = sock:send(\"hello world\\\\n\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n            else\n                ngx.say(\"sent: \", bytes)\n            end\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local header, err = reader()\n            if not header then\n                ngx.say(\"failed to receive header: \", err)\n                return\n            end\n\n            for i = 1, 2 do\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive line: \", err)\n                    return\n                end\n                ngx.say(\"received: \", line)\n            end\n        ';\n    }\n\n    location = /back {\n        content_by_lua '\n            ngx.send_headers()\n            ngx.flush(true)\n\n            local sock, err = ngx.req.socket()\n\n            if not sock then\n               ngx.say(\"failed to get socket: \", err)\n               return nil\n            end\n\n            sock:settimeout(100);\n\n            local reader = sock:receiveuntil(\"no-such-terminator\")\n            local data, err, partial = reader()\n            if not data then\n               ngx.say(\"err: \", err, \", partial: \", partial)\n            else\n                ngx.say(\"received: \", data)\n            end\n\n            ngx.sleep(0.1)\n\n            local data, err, partial = sock:receive()\n            if err then\n               ngx.say(\"err: \", err, \", partial: \", partial)\n            else\n                ngx.say(\"received: \", data)\n            end\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nsent: 65\nsent: 12\nreceived: err: timeout, partial: abc\nreceived: received: hello world\n\n--- error_log\nlua tcp socket read timed out\n\n\n\n=== TEST 17: req socket GC'd\n--- config\n    location /t {\n        content_by_lua '\n            do\n                local sock, err = ngx.req.socket()\n                if sock then\n                    ngx.say(\"got the request socket\")\n                else\n                    ngx.say(\"failed to get the request socket: \", err)\n                end\n            end\n            collectgarbage()\n            ngx.log(ngx.WARN, \"GC cycle done\")\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nPOST /t\nhello world\n--- response_body\ngot the request socket\ndone\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/lua finalize socket|GC cycle done/\n--- grep_error_log_out\nlua finalize socket\nGC cycle done\n\n\n\n=== TEST 18: receiveany\n--- config\n    location = /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", ngx.var.server_port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"POST /back HTTP/1.0\\r\\nHost: localhost\\r\\nContent-Length: 1024\\r\\n\\r\\nabc\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n            end\n\n            ngx.sleep(0.2)\n\n            local bytes, err = sock:send(\"hello world\\n\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n            end\n\n            local reader = sock:receiveuntil(\"\\r\\n\\r\\n\")\n            local header, err = reader()\n            if not header then\n                ngx.say(\"failed to receive header: \", err)\n                return\n            end\n\n            local line, err = sock:receive()\n            if not line then\n                ngx.say(\"failed to receive line: \", err)\n                return\n            end\n            ngx.say(\"received: \", line)\n        }\n    }\n\n    location = /back {\n        content_by_lua_block {\n            ngx.send_headers()\n            ngx.flush(true)\n\n            local sock, err = ngx.req.socket()\n\n            if not sock then\n               ngx.say(\"failed to get socket: \", err)\n               return nil\n            end\n\n            local data, err = sock:receiveany(4096)\n            if not data then\n               ngx.say(\"err: \", err)\n               return nil\n            end\n\n            ngx.say(\"received: \", data)\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nreceived: received: abc\n--- no_error_log\n[error]\n--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne \"\")\n\n\n\n=== TEST 19: getfd\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;;\";\n--- config\n    location /t {\n        content_by_lua_block {\n            local sock, err = ngx.req.socket()\n            if sock then\n                ngx.say(\"got the request socket\")\n            else\n                ngx.say(\"failed to get the request socket: \", err)\n            end\n\n            ngx.say(sock:getfd())\n        }\n    }\n--- request\nPOST /t\nhello world\n--- response_body eval\nqr/\\Agot the request socket\n\\d+\n\\z/ms\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/068-socket-keepalive.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#repeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4 + 34);\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir;\n$ENV{TEST_NGINX_REDIS_PORT} ||= 6379;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\n$ENV{LUA_PATH} ||=\n    '/usr/local/openresty-debug/lualib/?.lua;/usr/local/openresty/lualib/?.lua;;';\n\nno_long_string();\n#no_diff();\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go(port)\n            test.go(port)\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- request\nGET /t\n--- response_body\nconnected: 1, reused: 0\nrequest sent: 11\nreceived: OK\nconnected: 1, reused: 1\nrequest sent: 11\nreceived: OK\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive: free connection pool for \"]\n--- error_log eval\nqq{lua tcp socket get keepalive peer: using connection\nlua tcp socket keepalive create connection pool for key \"127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}\"\n}\n\n\n\n=== TEST 2: free up the whole connection pool if no active connections\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go(port, true)\n            test.go(port, false)\n        ';\n    }\n--- request\nGET /t\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port, keepalive)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    if keepalive then\n        local ok, err = sock:setkeepalive()\n        if not ok then\n            ngx.say(\"failed to set reusable: \", err)\n        end\n\n    else\n        sock:close()\n    end\nend\n--- response_body\nconnected: 1, reused: 0\nrequest sent: 11\nreceived: OK\nconnected: 1, reused: 1\nrequest sent: 11\nreceived: OK\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket get keepalive peer: using connection\",\n\"lua tcp socket keepalive: free connection pool for \"]\n\n\n\n=== TEST 3: upstream sockets close prematurely\n--- no_http3\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n   server_tokens off;\n   keepalive_timeout 100ms;\n   location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, err = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket keepalive close handler\",\n\"lua tcp socket keepalive: free connection pool for \"]\n--- timeout: 3\n\n\n\n=== TEST 4: http keepalive\n--- quic_max_idle_timeout: 1.2\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n   server_tokens off;\n   location /t {\n        keepalive_timeout 60s;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, err = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive close handler: fd:\",\n\"lua tcp socket keepalive: free connection pool for \"]\n--- timeout: 4\n\n\n\n=== TEST 5: lua_socket_keepalive_timeout\n--- quic_max_idle_timeout: 1.2\n--- config\n   server_tokens off;\n   location /t {\n       keepalive_timeout 60s;\n       lua_socket_keepalive_timeout 100ms;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, res = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket keepalive close handler\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket keepalive timeout: 100 ms\",\nqr/lua tcp socket connection pool size: 30\\b/]\n--- timeout: 4\n\n\n\n=== TEST 6: lua_socket_pool_size\n--- quic_max_idle_timeout: 1.2\n--- config\n   server_tokens off;\n   location /t {\n       keepalive_timeout 60s;\n       lua_socket_keepalive_timeout 100ms;\n       lua_socket_pool_size 1;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, res = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket keepalive close handler\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket keepalive timeout: 100 ms\",\nqr/lua tcp socket connection pool size: 1\\b/]\n--- timeout: 4\n\n\n\n=== TEST 7: \"lua_socket_keepalive_timeout 0\" means unlimited\n--- quic_max_idle_timeout: 1.2\n--- config\n   server_tokens off;\n   location /t {\n       keepalive_timeout 60s;\n       lua_socket_keepalive_timeout 0;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, res = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket keepalive timeout: unlimited\",\nqr/lua tcp socket connection pool size: 30\\b/]\n--- timeout: 4\n\n\n\n=== TEST 8: setkeepalive(timeout) overrides lua_socket_keepalive_timeout\n--- quic_max_idle_timeout: 1.2\n--- config\n   server_tokens off;\n   location /t {\n        keepalive_timeout 60s;\n        lua_socket_keepalive_timeout 60s;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, res = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive(123)\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket keepalive close handler\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket keepalive timeout: 123 ms\",\nqr/lua tcp socket connection pool size: 30\\b/]\n--- timeout: 4\n\n\n\n=== TEST 9: sock:setkeepalive(timeout, size) overrides lua_socket_pool_size\n--- quic_max_idle_timeout: 1.2\n--- config\n   server_tokens off;\n   location /t {\n       keepalive_timeout 60s;\n       lua_socket_keepalive_timeout 100ms;\n       lua_socket_pool_size 100;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, res = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive(101, 25)\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket keepalive close handler\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket keepalive timeout: 101 ms\",\nqr/lua tcp socket connection pool size: 25\\b/]\n--- timeout: 4\n\n\n\n=== TEST 10: setkeepalive() 'pool_size' should be greater than zero\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua_block {\n            local sock, err = ngx.socket.connect(\"127.0.0.1\", ngx.var.port)\n            if not sock then\n                ngx.say(err)\n                return\n            end\n\n            local ok, err = pcall(sock.setkeepalive, sock, 0, 0)\n            if not ok then\n                ngx.say(err)\n                return\n            end\n            ngx.say(ok)\n        }\n    }\n--- request\nGET /t\n--- response_body\nbad argument #3 to '?' (bad \"pool_size\" option value: 0)\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: sock:keepalive_timeout(0) means unlimited\n--- quic_max_idle_timeout: 1.2\n--- config\n   server_tokens off;\n   location /t {\n       keepalive_timeout 60s;\n       lua_socket_keepalive_timeout 1000ms;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: keepalive\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n0\\\\r\\\\n\\\\r\\\\n\")\n            local data, res = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive(0)\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        ';\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket keepalive timeout: unlimited\",\nqr/lua tcp socket connection pool size: 30\\b/]\n--- timeout: 4\n\n\n\n=== TEST 12: sanity (uds)\n--- http_config eval\n\"\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n    server {\n        listen unix:$::HtmlDir/nginx.sock;\n        default_type 'text/plain';\n\n        server_tokens off;\n        location /foo {\n            echo foo;\n            more_clear_headers Date;\n        }\n    }\n\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local path = \"$TEST_NGINX_HTML_DIR/nginx.sock\";\n            local port = ngx.var.port\n            test.go(path, port)\n            test.go(path, port)\n        ';\n    }\n--- request\nGET /t\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(path, port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"unix:\" .. path)\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"GET /foo HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: keepalive\\r\\n\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n    ngx.say(\"request sent: \", bytes)\n\n    local reader = sock:receiveuntil(\"\\r\\n0\\r\\n\\r\\n\")\n    local data, err = reader()\n\n    if not data then\n        ngx.say(\"failed to receive response body: \", err)\n        return\n    end\n\n    ngx.say(\"received response of \", #data, \" bytes\")\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- response_body\nconnected: 1, reused: 0\nrequest sent: 61\nreceived response of 119 bytes\nconnected: 1, reused: 1\nrequest sent: 61\nreceived response of 119 bytes\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive: free connection pool for \"]\n--- error_log eval\n[\"lua tcp socket get keepalive peer: using connection\",\n'lua tcp socket keepalive create connection pool for key \"unix:']\n\n\n\n=== TEST 13: github issue #108: ngx.location.capture + redis.set_keepalive\n--- http_config eval\n    qq{\n        lua_package_path \"$::HtmlDir/?.lua;;\";\n    }\n--- config\n    location /t {\n        default_type text/html;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        #lua_code_cache off;\n        lua_need_request_body on;\n        content_by_lua_file html/t.lua;\n    }\n\n    location /anyurl {\n        internal;\n        proxy_pass http://127.0.0.1:$server_port/dummy;\n    }\n\n    location = /dummy {\n        echo dummy;\n    }\n--- user_files\n>>> t.lua\nlocal sock, err = ngx.socket.connect(\"127.0.0.1\", ngx.var.port)\nif not sock then ngx.say(err) return end\nsock:send(\"flush_all\\r\\n\")\nsock:receive()\nsock:setkeepalive()\n\nsock, err = ngx.socket.connect(\"127.0.0.1\", ngx.var.port)\nif not sock then ngx.say(err) return end\nlocal res = ngx.location.capture(\"/anyurl\") --3\n\nngx.say(\"ok\")\n--- request\n    GET /t\n--- response_body\nok\n--- error_log\nlua tcp socket get keepalive peer: using connection\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 14: github issue #110: ngx.exit with HTTP_NOT_FOUND causes worker process to exit\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    error_page 404 /404.html;\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        access_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go(port)\n            ngx.exit(404)\n        ';\n        echo hello;\n    }\n--- user_files\n>>> 404.html\nNot found, dear...\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        ngx.log(ngx.ERR, \"failed to connect: \", err)\n        return\n    end\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.log(ngx.ERR, \"failed to send request: \", err)\n        return\n    end\n\n    local line, err, part = sock:receive()\n    if not line then\n        ngx.log(ngx.ERR, \"failed to receive a line: \", err, \" [\", part, \"]\")\n        return\n    end\n\n    -- local ok, err = sock:setkeepalive()\n    -- if not ok then\n        -- ngx.log(ngx.ERR, \"failed to set reusable: \", err)\n        -- return\n    -- end\nend\n--- request\nGET /t\n--- response_body\nNot found, dear...\n--- error_code: 404\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: custom pools (different pool for the same host:port) - tcp\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go(port, \"A\")\n            test.go(port, \"B\")\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port, pool)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port, {pool = pool})\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- request\nGET /t\n--- response_body\nconnected: 1, reused: 0\nconnected: 1, reused: 0\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket get keepalive peer: using connection\"\n]\n--- error_log\nlua tcp socket keepalive create connection pool for key \"A\"\nlua tcp socket keepalive create connection pool for key \"B\"\n\n\n\n=== TEST 16: custom pools (same pool for different host:port) - tcp\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go($TEST_NGINX_MEMCACHED_PORT, \"foo\")\n            test.go($TEST_NGINX_SERVER_PORT, \"foo\")\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port, pool)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port, {pool = pool})\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- request\nGET /t\n--- response_body\nconnected: 1, reused: 0\nconnected: 1, reused: 1\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive: free connection pool for \",\n]\n--- error_log\nlua tcp socket keepalive create connection pool for key \"foo\"\nlua tcp socket get keepalive peer: using connection\n\n\n\n=== TEST 17: custom pools (different pool for the same host:port) - unix\n--- http_config eval\n\"\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n    server {\n        listen unix:$::HtmlDir/nginx.sock;\n        default_type 'text/plain';\n\n        server_tokens off;\n        location /foo {\n            echo foo;\n            more_clear_headers Date;\n        }\n    }\n\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local path = \"$TEST_NGINX_HTML_DIR/nginx.sock\";\n            test.go(path, \"A\")\n            test.go(path, \"B\")\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(path, pool)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"unix:\" .. path, {pool = pool})\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- request\nGET /t\n--- response_body\nconnected: 1, reused: 0\nconnected: 1, reused: 0\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket get keepalive peer: using connection\"\n]\n--- error_log\nlua tcp socket keepalive create connection pool for key \"A\"\nlua tcp socket keepalive create connection pool for key \"B\"\n\n\n\n=== TEST 18: custom pools (same pool for the same path) - unix\n--- http_config eval\n\"\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n    server {\n        listen unix:$::HtmlDir/nginx.sock;\n        default_type 'text/plain';\n\n        server_tokens off;\n    }\n\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local path = \"$TEST_NGINX_HTML_DIR/nginx.sock\";\n            test.go(path, \"A\")\n            test.go(path, \"A\")\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(path, pool)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"unix:\" .. path, {pool = pool})\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- request\nGET /t\n--- response_body\nconnected: 1, reused: 0\nconnected: 1, reused: 1\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive: free connection pool for \",\n]\n--- error_log\nlua tcp socket keepalive create connection pool for key \"A\"\nlua tcp socket get keepalive peer: using connection\n\n\n\n=== TEST 19: numeric pool option value\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go($TEST_NGINX_MEMCACHED_PORT, 3.14)\n            test.go($TEST_NGINX_SERVER_PORT, 3.14)\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port, pool)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port, {pool = pool})\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- request\nGET /t\n--- response_body\nconnected: 1, reused: 0\nconnected: 1, reused: 1\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive: free connection pool for \",\n]\n--- error_log\nlua tcp socket keepalive create connection pool for key \"3.14\"\nlua tcp socket get keepalive peer: using connection\n\n\n\n=== TEST 20: nil pool option value\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go($TEST_NGINX_MEMCACHED_PORT, nil)\n            test.go($TEST_NGINX_SERVER_PORT, nil)\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port, pool)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port, {pool = pool})\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- request\nGET /t\n--- response_body\nconnected: 1, reused: 0\nconnected: 1, reused: 0\n--- error_code: 200\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: (bad) table pool option value\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go($TEST_NGINX_MEMCACHED_PORT, {})\n            test.go($TEST_NGINX_SERVER_PORT, {})\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port, pool)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port, {pool = pool})\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- request\nGET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad argument #3 to 'connect' (bad \"pool\" option type: table)\n\n\n\n=== TEST 22: (bad) boolean pool option value\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go($TEST_NGINX_MEMCACHED_PORT, true)\n            test.go($TEST_NGINX_SERVER_PORT, false)\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port, pool)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port, {pool = pool})\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- request\nGET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad argument #3 to 'connect' (bad \"pool\" option type: boolean)\n\n\n\n=== TEST 23: clear the redis store\n--- no_http2\n--- config\n    location /t {\n        redis2_query flushall;\n        redis2_pass 127.0.0.1:$TEST_NGINX_REDIS_PORT;\n    }\n--- request\n    GET /t\n--- response_body eval\n\"+OK\\r\\n\"\n--- no_error_log\n[error]\n[alert]\n[warn]\n\n\n\n=== TEST 24: bug in send(): clear the chain writer ctx\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_REDIS_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local port = ngx.var.port\n            test.go(port)\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    local bytes, err = sock:send({})\n    if err then\n        ngx.say(\"failed to send empty request: \", err)\n        return\n    end\n\n    local req = \"*2\\r\\n$3\\r\\nget\\r\\n$3\\r\\ndog\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        ngx.say(\"failed to send request: \", err)\n        return\n    end\n\n    local line, err, part = sock:receive()\n    if line then\n        ngx.say(\"received: \", line)\n\n    else\n        ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\n\n    ngx.say(\"done\")\nend\n--- request\nGET /t\n--- stap2\nglobal active\nM(http-lua-socket-tcp-send-start) {\n    active = 1\n    printf(\"send [%s] %d\\n\", text_str(user_string_n($arg3, $arg4)), $arg4)\n}\nM(http-lua-socket-tcp-receive-done) {\n    printf(\"receive [%s]\\n\", text_str(user_string_n($arg3, $arg4)))\n}\nF(ngx_output_chain) {\n    #printf(\"ctx->in: %s\\n\", ngx_chain_dump($ctx->in))\n    #printf(\"ctx->busy: %s\\n\", ngx_chain_dump($ctx->busy))\n    printf(\"output chain: %s\\n\", ngx_chain_dump($in))\n}\nF(ngx_linux_sendfile_chain) {\n    printf(\"linux sendfile chain: %s\\n\", ngx_chain_dump($in))\n}\nF(ngx_chain_writer) {\n    printf(\"chain writer ctx out: %p\\n\", $data)\n    printf(\"nginx chain writer: %s\\n\", ngx_chain_dump($in))\n}\nF(ngx_http_lua_socket_tcp_setkeepalive) {\n    delete active\n}\nM(http-lua-socket-tcp-setkeepalive-buf-unread) {\n    printf(\"setkeepalive unread: [%s]\\n\", text_str(user_string_n($arg3, $arg4)))\n}\nprobe syscall.recvfrom {\n    if (active && pid() == target()) {\n        printf(\"recvfrom(%s)\", argstr)\n    }\n}\nprobe syscall.recvfrom.return {\n    if (active && pid() == target()) {\n        printf(\" = %s, data [%s]\\n\", retstr, text_str(user_string_n($ubuf, $size)))\n    }\n}\nprobe syscall.writev {\n    if (active && pid() == target()) {\n        printf(\"writev(%s)\", ngx_iovec_dump($vec, $vlen))\n        /*\n        for (i = 0; i < $vlen; i++) {\n            printf(\" %p [%s]\", $vec[i]->iov_base, text_str(user_string_n($vec[i]->iov_base, $vec[i]->iov_len)))\n        }\n        */\n    }\n}\nprobe syscall.writev.return {\n    if (active && pid() == target()) {\n        printf(\" = %s\\n\", retstr)\n    }\n}\n--- response_body\nreceived: $-1\ndone\n--- no_error_log\n[error]\n\n\n\n=== TEST 25: setkeepalive() with explicit nil args\n--- quic_max_idle_timeout: 1.2\n--- config\n   server_tokens off;\n   location /t {\n       lua_socket_keepalive_timeout 100ms;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: keepalive\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\r\\n0\\r\\n\\r\\n\")\n            local data, res = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            local ok, err = sock:setkeepalive(nil, nil)\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"done\")\n        }\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 61\nreceived response of 156 bytes\ndone\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua tcp socket keepalive close handler\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket keepalive timeout: 100 ms\",\nqr/lua tcp socket connection pool size: 30\\b/]\n--- timeout: 4\n\n\n\n=== TEST 26: conn queuing: connect() verifies the options for connection pool\n--- config\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local function check_opts_for_connect(opts)\n                local ok, err = pcall(function()\n                    sock:connect(\"127.0.0.1\", ngx.var.port, opts)\n                end)\n                if not ok then\n                    ngx.say(err)\n                else\n                    ngx.say(\"ok\")\n                end\n            end\n\n            check_opts_for_connect({pool_size = 'a'})\n            check_opts_for_connect({pool_size = 0})\n            check_opts_for_connect({backlog = -1})\n            check_opts_for_connect({backlog = 0})\n        }\n    }\n--- request\nGET /t\n--- response_body_like\n.+ 'connect' \\(bad \"pool_size\" option type: string\\)\n.+ 'connect' \\(bad \"pool_size\" option value: 0\\)\n.+ 'connect' \\(bad \"backlog\" option value: -1\\)\nok\n--- no_error_log\n[error]\n\n\n\n=== TEST 27: conn queuing: connect() can specify 'pool_size' which overrides setkeepalive()\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local function go()\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(\"127.0.0.1\", port, {pool_size = 1})\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n                local req = \"flush_all\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send request: \", err)\n                    return\n                end\n                ngx.say(\"request sent: \", bytes)\n\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                end\n\n                local ok, err = sock:setkeepalive(0, 20)\n                if not ok then\n                    ngx.say(\"failed to set reusable: \", err)\n                end\n            end\n\n            -- reuse ok\n            go()\n            go()\n\n            local sock1 = ngx.socket.connect(\"127.0.0.1\", port)\n            local sock2 = ngx.socket.connect(\"127.0.0.1\", port)\n            local ok, err = sock1:setkeepalive(0, 20)\n            if not ok then\n                ngx.say(err)\n            end\n            local ok, err = sock2:setkeepalive(0, 20)\n            if not ok then\n                ngx.say(err)\n            end\n\n            -- the pool_size is 1 instead of 20\n            sock1 = ngx.socket.connect(\"127.0.0.1\", port)\n            sock2 = ngx.socket.connect(\"127.0.0.1\", port)\n            ngx.say(\"reused: \", sock1:getreusedtimes())\n            ngx.say(\"reused: \", sock2:getreusedtimes())\n            sock1:setkeepalive(0, 20)\n            sock2:setkeepalive(0, 20)\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1, reused: 0\nrequest sent: 11\nreceived: OK\nconnected: 1, reused: 1\nrequest sent: 11\nreceived: OK\nreused: 1\nreused: 0\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket connection pool size: 20\"]\n--- error_log eval\n[qq{lua tcp socket keepalive create connection pool for key \"127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}\"},\n\"lua tcp socket connection pool size: 1\",\n]\n\n\n\n=== TEST 28: conn queuing: connect() can specify 'pool_size' for unix domain socket\n--- http_config eval\n\"\n    server {\n        listen unix:$::HtmlDir/nginx.sock;\n    }\n\"\n--- config\n    location /t {\n        content_by_lua_block {\n            local path = \"unix:\" .. \"$TEST_NGINX_HTML_DIR/nginx.sock\";\n            local function go()\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(path, {pool_size = 1})\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n                local ok, err = sock:setkeepalive(0, 20)\n                if not ok then\n                    ngx.say(\"failed to set reusable: \", err)\n                end\n            end\n\n            go()\n            go()\n\n            local sock1 = ngx.socket.connect(path)\n            local sock2 = ngx.socket.connect(path)\n            local ok, err = sock1:setkeepalive(0, 20)\n            if not ok then\n                ngx.say(err)\n            end\n            local ok, err = sock2:setkeepalive(0, 20)\n            if not ok then\n                ngx.say(err)\n            end\n\n            -- the pool_size is 1 instead of 20\n            sock1 = ngx.socket.connect(path)\n            sock2 = ngx.socket.connect(path)\n            ngx.say(\"reused: \", sock1:getreusedtimes())\n            ngx.say(\"reused: \", sock2:getreusedtimes())\n            sock1:setkeepalive(0, 20)\n            sock2:setkeepalive(0, 20)\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1, reused: 0\nconnected: 1, reused: 1\nreused: 1\nreused: 0\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket connection pool size: 20\"]\n--- error_log eval\n[\"lua tcp socket get keepalive peer: using connection\",\n'lua tcp socket keepalive create connection pool for key \"unix:',\n\"lua tcp socket connection pool size: 1\",\n]\n\n\n\n=== TEST 29: conn queuing: connect() can specify 'pool_size' for custom pool\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local function go(pool)\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(\"127.0.0.1\", port, {pool = pool, pool_size = 1})\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", pool, \", reused: \", sock:getreusedtimes())\n\n                local ok, err = sock:setkeepalive(0, 20)\n                if not ok then\n                    ngx.say(\"failed to set reusable: \", err)\n                end\n            end\n\n            go('A')\n            go('B')\n            go('A')\n            go('B')\n\n            local sock1 = ngx.socket.connect(\"127.0.0.1\", port, {pool = 'A'})\n            local sock2 = ngx.socket.connect(\"127.0.0.1\", port, {pool = 'A'})\n            local ok, err = sock1:setkeepalive(0, 20)\n            if not ok then\n                ngx.say(err)\n            end\n            local ok, err = sock2:setkeepalive(0, 20)\n            if not ok then\n                ngx.say(err)\n            end\n\n            -- the pool_size is 1 instead of 20\n            sock1 = ngx.socket.connect(\"127.0.0.1\", port, {pool = 'A'})\n            sock2 = ngx.socket.connect(\"127.0.0.1\", port, {pool = 'A'})\n            ngx.say(\"reused: \", sock1:getreusedtimes())\n            ngx.say(\"reused: \", sock2:getreusedtimes())\n            sock1:setkeepalive(0, 20)\n            sock2:setkeepalive(0, 20)\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: A, reused: 0\nconnected: B, reused: 0\nconnected: A, reused: 1\nconnected: B, reused: 1\nreused: 1\nreused: 0\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket connection pool size: 20\"]\n--- error_log eval\n[qq{lua tcp socket keepalive create connection pool for key \"A\"},\nqq{lua tcp socket keepalive create connection pool for key \"B\"},\n\"lua tcp socket connection pool size: 1\",\n]\n\n\n\n=== TEST 30: conn queuing: connect() uses lua_socket_pool_size as default if 'backlog' is given\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        lua_socket_pool_size 1234;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {backlog = 0}\n            local sock, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            if not sock then\n                ngx.say(err)\n            else\n                ngx.say(\"ok\")\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- error_log\nlua tcp socket connection pool size: 1234\n--- no_error_log\n[error]\n\n\n\n=== TEST 31: conn queuing: more connect operations than 'backlog' size\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool_size = 2, backlog = 0}\n            local sock = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            local not_reused_socket, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            if not not_reused_socket then\n                ngx.say(err)\n                return\n            end\n            -- burst\n            local ok, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            if not ok then\n                ngx.say(err)\n            end\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(err)\n                return\n            end\n\n            ok, err = sock:connect(\"127.0.0.1\", port, opts)\n            if not ok then\n                ngx.say(err)\n            end\n            ngx.say(\"reused: \", sock:getreusedtimes())\n            -- both queue and pool is full\n            ok, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            if not ok then\n                ngx.say(err)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\ntoo many waiting connect operations\nreused: 1\ntoo many waiting connect operations\n--- no_error_log\n[error]\n\n\n\n=== TEST 32: conn queuing: once 'pool_size' is reached and pool has 'backlog'\n--- quic_max_idle_timeout: 1.2\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool_size = 2, backlog = 2}\n            local sock1 = ngx.socket.connect(\"127.0.0.1\", port, opts)\n\n            ngx.timer.at(0, function(premature)\n                local sock2, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n                if not sock2 then\n                    ngx.log(ngx.ERR, err)\n                    return\n                end\n\n                ngx.log(ngx.WARN, \"start to handle timer\")\n                ngx.sleep(0.1)\n                sock2:close()\n                -- resume connect operation\n                ngx.log(ngx.WARN, \"continue to handle timer\")\n            end)\n\n            ngx.sleep(0.05)\n            ngx.log(ngx.WARN, \"start to handle cosocket\")\n            local sock3, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            if not sock3 then\n                ngx.say(err)\n                return\n            end\n            ngx.log(ngx.WARN, \"continue to handle cosocket\")\n\n            local req = \"flush_all\\r\\n\"\n            local bytes, err = sock3:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local line, err, part = sock3:receive()\n            if line then\n                ngx.say(\"received: \", line)\n            else\n                ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n            end\n\n            local ok, err = sock3:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n            ngx.say(\"setkeepalive: OK\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nrequest sent: 11\nreceived: OK\nsetkeepalive: OK\n--- no_error_log\n[error]\n--- error_log\nlua tcp socket queue connect operation for connection pool \"127.0.0.1\n--- grep_error_log eval: qr/(start|continue) to handle \\w+/\n--- grep_error_log_out\nstart to handle timer\nstart to handle cosocket\ncontinue to handle timer\ncontinue to handle cosocket\n\n\n\n=== TEST 33: conn queuing: do not count failed connect operations\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool = \"test\", pool_size = 1, backlog = 0}\n\n            local sock = ngx.socket.tcp()\n            sock:settimeouts(100, 3000, 3000)\n            local ok, err = sock:connect(\"127.0.0.2\", 12345, opts)\n            if not ok then\n                ngx.say(err)\n            end\n\n            local sock, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            if not sock then\n                ngx.say(err)\n            end\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- error_log\nlua tcp socket connect timed out, upstream: 127.0.0.2:12345(127.0.0.2)\n--- response_body\ntimeout\nok\n\n\n\n=== TEST 34: conn queuing: connect until backlog is reached\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool_size = 1, backlog = 1}\n            local sock1 = ngx.socket.connect(\"127.0.0.1\", port, opts)\n\n            ngx.timer.at(0.01, function(premature)\n                ngx.log(ngx.WARN, \"start to handle timer\")\n                local sock2, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n                if not sock2 then\n                    ngx.log(ngx.ERR, err)\n                    return\n                end\n\n                ngx.sleep(0.02)\n                local ok, err = sock2:close()\n                if not ok then\n                    ngx.log(ngx.ERR, err)\n                end\n                ngx.log(ngx.WARN, \"continue to handle timer\")\n            end)\n\n            ngx.sleep(0.02)\n            local sock3, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            if not sock3 then\n                ngx.say(err)\n            end\n            local ok, err = sock1:setkeepalive()\n            if not ok then\n                ngx.say(err)\n                return\n            end\n            ngx.sleep(0.01) -- run sock2\n\n            ngx.log(ngx.WARN, \"start to handle cosocket\")\n            local sock3, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            if not sock3 then\n                ngx.say(err)\n                return\n            end\n            ngx.log(ngx.WARN, \"continue to handle cosocket\")\n\n            local ok, err = sock3:setkeepalive()\n            if not ok then\n                ngx.say(err)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\ntoo many waiting connect operations\n--- no_error_log\n[error]\n--- error_log\nlua tcp socket queue connect operation for connection pool \"127.0.0.1\n--- grep_error_log eval: qr/queue connect operation for connection pool|(start|continue) to handle \\w+/\n--- grep_error_log_out\nstart to handle timer\nqueue connect operation for connection pool\nstart to handle cosocket\nqueue connect operation for connection pool\ncontinue to handle timer\ncontinue to handle cosocket\n\n\n\n=== TEST 35: conn queuing: memory reuse for host in queueing connect operation ctx\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool = \"test\", pool_size = 1, backlog = 3}\n            local sock = ngx.socket.connect(\"127.0.0.1\", port, opts)\n\n            ngx.timer.at(0.01, function(premature)\n                local sock, err = ngx.socket.connect(\"0.0.0.0\", port, opts)\n                if not sock then\n                    ngx.log(ngx.ERR, err)\n                    return\n                end\n\n                local ok, err = sock:close()\n                if not ok then\n                    ngx.log(ngx.ERR, err)\n                end\n            end)\n\n            ngx.timer.at(0.015, function(premature)\n                local sock, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n                if not sock then\n                    ngx.log(ngx.ERR, err)\n                    return\n                end\n\n                local ok, err = sock:close()\n                if not ok then\n                    ngx.log(ngx.ERR, err)\n                end\n            end)\n\n            ngx.timer.at(0.02, function(premature)\n                local sock, err = ngx.socket.connect(\"0.0.0.0\", port, opts)\n                if not sock then\n                    ngx.log(ngx.ERR, err)\n                    return\n                end\n\n                local ok, err = sock:close()\n                if not ok then\n                    ngx.log(ngx.ERR, err)\n                end\n            end)\n\n            ngx.sleep(0.03)\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(err)\n                return\n            end\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/queue connect operation for connection pool/\n--- grep_error_log_out\nqueue connect operation for connection pool\nqueue connect operation for connection pool\nqueue connect operation for connection pool\n\n\n\n=== TEST 36: conn queuing: connect() returns error after connect operation resumed\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool = \"test\", pool_size = 1, backlog = 1}\n            local sock = ngx.socket.connect(\"127.0.0.1\", port, opts)\n\n            ngx.timer.at(0, function(premature)\n                local sock, err = ngx.socket.connect(\"\", port, opts)\n                if not sock then\n                    ngx.log(ngx.WARN, err)\n                end\n            end)\n\n            ngx.sleep(0.01)\n            -- use 'close' to force parsing host instead of reusing conn\n            local ok, err = sock:close()\n            if not ok then\n                ngx.say(err)\n                return\n            end\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- no_error_log\n[error]\n--- error_log\nfailed to parse host name\n--- grep_error_log eval: qr/queue connect operation for connection pool/\n--- grep_error_log_out\nqueue connect operation for connection pool\n\n\n\n=== TEST 37: conn queuing: in uthread\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool_size = 1, backlog = 2}\n\n            local conn_sock = function()\n                local sock, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n                if not sock then\n                    ngx.say(err)\n                    return\n                end\n                ngx.say(\"start to handle uthread\")\n\n                ngx.sleep(0.01)\n                sock:close()\n                ngx.say(\"continue to handle other uthread\")\n            end\n\n            local sock, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            if not sock then\n                ngx.log(ngx.ERR, err)\n                return\n            end\n\n            local co1 = ngx.thread.spawn(conn_sock)\n            local co2 = ngx.thread.spawn(conn_sock)\n            local co3 = ngx.thread.spawn(conn_sock)\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.log(ngx.ERR, err)\n            end\n\n            ngx.thread.wait(co1)\n            ngx.thread.wait(co2)\n            ngx.thread.wait(co3)\n            ngx.say(\"all uthreads ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntoo many waiting connect operations\nstart to handle uthread\ncontinue to handle other uthread\nstart to handle uthread\ncontinue to handle other uthread\nall uthreads ok\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/queue connect operation for connection pool/\n--- grep_error_log_out\nqueue connect operation for connection pool\nqueue connect operation for connection pool\n\n\n\n=== TEST 38: conn queuing: in access_by_lua\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        access_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool_size = 1, backlog = 2}\n\n            local conn_sock = function()\n                local sock, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n                if not sock then\n                    ngx.say(err)\n                    return\n                end\n                ngx.say(\"start to handle uthread\")\n\n                ngx.sleep(0.01)\n                sock:close()\n                ngx.say(\"continue to handle other uthread\")\n            end\n\n            local sock, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            if not sock then\n                ngx.log(ngx.ERR, err)\n                return\n            end\n\n            local co1 = ngx.thread.spawn(conn_sock)\n            local co2 = ngx.thread.spawn(conn_sock)\n            local co3 = ngx.thread.spawn(conn_sock)\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.log(ngx.ERR, err)\n            end\n\n            ngx.thread.wait(co1)\n            ngx.thread.wait(co2)\n            ngx.thread.wait(co3)\n            ngx.say(\"all uthreads ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntoo many waiting connect operations\nstart to handle uthread\ncontinue to handle other uthread\nstart to handle uthread\ncontinue to handle other uthread\nall uthreads ok\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/queue connect operation for connection pool/\n--- grep_error_log_out\nqueue connect operation for connection pool\nqueue connect operation for connection pool\n\n\n\n=== TEST 39: conn queuing: in rewrite_by_lua\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        rewrite_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool_size = 1, backlog = 2}\n\n            local conn_sock = function()\n                local sock, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n                if not sock then\n                    ngx.say(err)\n                    return\n                end\n                ngx.say(\"start to handle uthread\")\n\n                ngx.sleep(0.01)\n                sock:close()\n                ngx.say(\"continue to handle other uthread\")\n            end\n\n            local sock, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            if not sock then\n                ngx.log(ngx.ERR, err)\n                return\n            end\n\n            local co1 = ngx.thread.spawn(conn_sock)\n            local co2 = ngx.thread.spawn(conn_sock)\n            local co3 = ngx.thread.spawn(conn_sock)\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.log(ngx.ERR, err)\n            end\n\n            ngx.thread.wait(co1)\n            ngx.thread.wait(co2)\n            ngx.thread.wait(co3)\n            ngx.say(\"all uthreads ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntoo many waiting connect operations\nstart to handle uthread\ncontinue to handle other uthread\nstart to handle uthread\ncontinue to handle other uthread\nall uthreads ok\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/queue connect operation for connection pool/\n--- grep_error_log_out\nqueue connect operation for connection pool\nqueue connect operation for connection pool\n\n\n\n=== TEST 40: conn queuing: in subrequest\n--- config\n    set $port $TEST_NGINX_MEMCACHED_PORT;\n\n    location /t {\n        content_by_lua_block {\n            local port = ngx.var.port\n            ngx.timer.at(0, function()\n                local opts = {pool_size = 1, backlog = 2}\n                local sock, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n                if not sock then\n                    ngx.log(ngx.ERR, err)\n                    return\n                end\n\n                ngx.sleep(0.1)\n                local ok, err = sock:setkeepalive()\n                if not ok then\n                    ngx.log(ngx.ERR, err)\n                end\n            end)\n\n            ngx.sleep(0.01)\n            local res1, res2, res3 = ngx.location.capture_multi{\n                {\"/conn\"}, {\"/conn\"}, {\"/conn\"}\n            }\n            ngx.say(res1.body)\n            ngx.say(res2.body)\n            ngx.say(res3.body)\n        }\n    }\n\n    location /conn {\n        content_by_lua_block {\n            local port = ngx.var.port\n            local sock, err = ngx.socket.connect(\"127.0.0.1\", port)\n            if not sock then\n                ngx.print(err)\n                return\n            end\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.print(err)\n            else\n                ngx.print(\"ok\")\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\nok\ntoo many waiting connect operations\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/queue connect operation for connection pool/\n--- grep_error_log_out\nqueue connect operation for connection pool\nqueue connect operation for connection pool\n\n\n\n=== TEST 41: conn queuing: timeouts when 'connect_timeout' is reached\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool_size = 1, backlog = 1}\n            local sock1 = ngx.socket.connect(\"127.0.0.1\", port, opts)\n\n            local sock2 = ngx.socket.tcp()\n            sock2:settimeouts(10, 3000, 3000)\n            local ok, err = sock2:connect(\"127.0.0.1\", port, opts)\n            if not ok then\n                ngx.say(err)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\ntimeout\n--- error_log eval\n\"lua tcp socket queued connect timed out, when trying to connect to 127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}\"\n\n\n\n=== TEST 42: conn queuing: set timeout via lua_socket_connect_timeout\n--- config\n    lua_socket_connect_timeout 10ms;\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool_size = 1, backlog = 1}\n            local sock1 = ngx.socket.connect(\"127.0.0.1\", port, opts)\n\n            local sock2 = ngx.socket.tcp()\n            local ok, err = sock2:connect(\"127.0.0.1\", port, opts)\n            if not ok then\n                ngx.say(err)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\ntimeout\n--- error_log eval\n\"lua tcp socket queued connect timed out, when trying to connect to 127.0.0.1:$ENV{TEST_NGINX_MEMCACHED_PORT}\"\n\n\n\n=== TEST 43: conn queuing: client aborting while connect operation is queued\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool_size = 1, backlog = 1}\n            local sock1 = ngx.socket.connect(\"127.0.0.1\", port, opts)\n\n            local sock2 = ngx.socket.tcp()\n            sock2:settimeouts(3000, 3000, 3000)\n            local ok, err = sock2:connect(\"127.0.0.1\", port, opts)\n            if not ok then\n                ngx.say(err)\n            end\n        }\n    }\n--- request\nGET /t\n--- ignore_response\n--- timeout: 0.1\n--- abort\n--- no_error_log\n[error]\n--- curl_error eval\nqr/curl: \\(28\\) Operation timed out after \\d+ milliseconds with 0 bytes received/\n\n\n\n=== TEST 44: conn queuing: resume next connect operation if resumed connect failed immediately\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool = \"test\", pool_size = 1, backlog = 2}\n\n            local conn_sock = function(should_timeout)\n                local sock = ngx.socket.tcp()\n                local ok, err\n                if should_timeout then\n                    ok, err = sock:connect(\"\", port, opts)\n                else\n                    ok, err = sock:connect(\"127.0.0.1\", port, opts)\n                end\n                if not ok then\n                    ngx.say(err)\n                    return\n                end\n                ngx.say(\"connected in uthread\")\n                sock:close()\n            end\n\n            local sock, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            if not sock then\n                ngx.log(ngx.ERR, err)\n                return\n            end\n\n            local co1 = ngx.thread.spawn(conn_sock, true)\n            local co2 = ngx.thread.spawn(conn_sock)\n\n            local ok, err = sock:close()\n            if not ok then\n                ngx.log(ngx.ERR, err)\n            end\n\n            ngx.thread.wait(co1)\n            ngx.thread.wait(co2)\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nfailed to parse host name \"\": no host\nconnected in uthread\nok\n--- no_error_log\n[error]\n\n\n\n=== TEST 45: conn queuing: resume connect operation if resumed connect failed (timeout)\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool = \"test\", pool_size = 1, backlog = 1}\n\n            local conn_sock = function(should_timeout)\n                local sock = ngx.socket.tcp()\n                local ok, err\n                if should_timeout then\n                    sock:settimeouts(100, 3000, 3000)\n                    ok, err = sock:connect(\"127.0.0.2\", 12345, opts)\n                else\n                    ok, err = sock:connect(\"127.0.0.1\", port, opts)\n                end\n                if not ok then\n                    ngx.say(err)\n                    return\n                end\n                ngx.say(\"connected in uthread\")\n                sock:close()\n            end\n\n            local co1 = ngx.thread.spawn(conn_sock, true)\n            local co2 = ngx.thread.spawn(conn_sock)\n\n            ngx.thread.wait(co1)\n            ngx.thread.wait(co2)\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntimeout\nconnected in uthread\nok\n--- error_log\nqueue connect operation for connection pool \"test\"\nlua tcp socket connect timed out, upstream: 127.0.0.2:12345(127.0.0.2)\n\n\n\n=== TEST 46: conn queuing: resume connect operation if resumed connect failed (could not be resolved)\n--- quic_max_idle_timeout: 1.2\n--- config\n    resolver 127.0.0.2:12345 ipv6=off;\n    resolver_timeout 1s;\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool = \"test\", pool_size = 1, backlog = 1}\n\n            local conn_sock = function(should_timeout)\n                local sock = ngx.socket.tcp()\n                local ok, err\n                if should_timeout then\n                    sock:settimeouts(1, 3000, 3000)\n                    ok, err = sock:connect(\"agentzh.org\", 80, opts)\n                else\n                    ok, err = sock:connect(\"127.0.0.1\", port, opts)\n                end\n                if not ok then\n                    ngx.say(err)\n                    return\n                end\n                ngx.say(\"connected in uthread\")\n                sock:close()\n            end\n\n            local co1 = ngx.thread.spawn(conn_sock, true)\n            local co2 = ngx.thread.spawn(conn_sock)\n\n            ngx.thread.wait(co1)\n            ngx.thread.wait(co2)\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nagentzh.org could not be resolved (110: Operation timed out)\nconnected in uthread\nok\n--- error_log\nqueue connect operation for connection pool \"test\"\n\n\n\n=== TEST 47: conn queuing: resume connect operation if resumed connect failed (connection refused)\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local port = ngx.var.port\n            local opts = {pool = \"test\", pool_size = 1, backlog = 1}\n\n            local conn_sock = function(should_timeout)\n                local sock = ngx.socket.tcp()\n                local ok, err\n                if should_timeout then\n                    sock:settimeouts(100, 3000, 3000)\n                    ok, err = sock:connect(\"127.0.0.1\", 62345, opts)\n                else\n                    ok, err = sock:connect(\"127.0.0.1\", port, opts)\n                end\n                if not ok then\n                    ngx.say(err)\n                    return\n                end\n                ngx.say(\"connected in uthread\")\n                sock:close()\n            end\n\n            local co1 = ngx.thread.spawn(conn_sock, true)\n            local co2 = ngx.thread.spawn(conn_sock)\n\n            ngx.thread.wait(co1)\n            ngx.thread.wait(co2)\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnection refused\nconnected in uthread\nok\n--- error_log\nqueue connect operation for connection pool \"test\"\n\n\n\n=== TEST 48: conn queuing: resume connect operation if resumed connect failed (uthread aborted while resolving)\n--- http_config\n    lua_package_path '../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;';\n--- config\n    resolver 127.0.0.1 ipv6=off;\n    resolver_timeout 100s;\n    set $port $TEST_NGINX_MEMCACHED_PORT;\n\n    location /sub {\n        content_by_lua_block {\n            local semaphore = require \"ngx.semaphore\"\n            local sem = semaphore.new()\n\n            local function f()\n                sem:wait(0.1)\n                ngx.exit(0)\n            end\n\n            local opts = {pool = \"test\", pool_size = 1, backlog = 1}\n            local port = ngx.var.port\n            ngx.timer.at(0, function()\n                sem:post()\n                local sock2, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n                package.loaded.for_timer_to_resume:post()\n                if not sock2 then\n                    ngx.log(ngx.ALERT, \"resume connect failed: \", err)\n                    return\n                end\n\n                ngx.log(ngx.INFO, \"resume success\")\n            end)\n\n            ngx.thread.spawn(f)\n            local sock1, err = ngx.socket.connect(\"openresty.org\", 80, opts)\n            if not sock1 then\n                ngx.say(err)\n                return\n            end\n        }\n    }\n\n    location /t {\n        content_by_lua_block {\n            local semaphore = require \"ngx.semaphore\"\n            local for_timer_to_resume = semaphore.new()\n            package.loaded.for_timer_to_resume = for_timer_to_resume\n\n            ngx.location.capture(\"/sub\")\n            for_timer_to_resume:wait(0.1)\n        }\n    }\n--- request\nGET /t\n--- no_error_log\n[alert]\n--- error_log\nresume success\n\n\n\n=== TEST 49: conn queuing: resume connect operation if resumed connect failed (uthread killed while resolving)\n--- config\n    resolver 127.0.0.1 ipv6=off;\n    resolver_timeout 100s;\n    set $port $TEST_NGINX_MEMCACHED_PORT;\n\n    location /t {\n        content_by_lua_block {\n            local opts = {pool = \"test\", pool_size = 1, backlog = 1}\n            local port = ngx.var.port\n\n            local function resolve()\n                local sock1, err = ngx.socket.connect(\"openresty.org\", 80, opts)\n                if not sock1 then\n                    ngx.say(err)\n                    return\n                end\n            end\n\n            local th = ngx.thread.spawn(resolve)\n            local ok, err = ngx.thread.kill(th)\n            if not ok then\n                ngx.log(ngx.ALERT, \"kill thread failed: \", err)\n                return\n            end\n\n            local sock2, err = ngx.socket.connect(\"127.0.0.1\", port, opts)\n            if not sock2 then\n                ngx.log(ngx.ALERT, \"resume connect failed: \", err)\n                return\n            end\n\n            ngx.log(ngx.INFO, \"resume success\")\n        }\n    }\n--- request\nGET /t\n--- no_error_log\n[alert]\n--- error_log\nresume success\n\n\n\n=== TEST 50: conn queuing: increase the counter for connections created before creating the pool with setkeepalive()\n--- config\n    set $port $TEST_NGINX_MEMCACHED_PORT;\n\n    location /t {\n        content_by_lua_block {\n            local function connect()\n                local sock, err = ngx.socket.connect(\"127.0.0.1\", ngx.var.port)\n                if not sock then\n                    error(\"connect failed: \" .. err)\n                end\n\n                return sock\n            end\n\n            local sock1 = connect()\n            local sock2 = connect()\n            assert(sock1:setkeepalive())\n            assert(sock2:setkeepalive())\n\n            local sock1 = connect()\n            local sock2 = connect()\n            assert(sock1:close())\n            assert(sock2:close())\n\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- no_error_log\n[error]\n--- response_body\nok\n\n\n\n=== TEST 51: conn queuing: only decrease the counter for connections which were counted by the pool\n--- config\n    set $port $TEST_NGINX_MEMCACHED_PORT;\n\n    location /t {\n        content_by_lua_block {\n            local function connect()\n                local sock, err = ngx.socket.connect(\"127.0.0.1\", ngx.var.port)\n                if not sock then\n                    error(\"connect failed: \" .. err)\n                end\n\n                return sock\n            end\n\n            local sock1 = connect()\n            local sock2 = connect()\n            assert(sock1:setkeepalive(1000, 1))\n            assert(sock2:setkeepalive(1000, 1))\n\n            local sock1 = connect()\n            local sock2 = connect()\n            assert(sock1:close())\n            assert(sock2:close())\n\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- no_error_log\n[error]\n--- response_body\nok\n\n\n\n=== TEST 52: conn queuing: clean up pending connect operations which are in queue\n--- config\n    set $port $TEST_NGINX_MEMCACHED_PORT;\n\n    location /sub {\n        content_by_lua_block {\n            local opts = {pool = \"test\", pool_size = 1, backlog = 1}\n            local sock, err = ngx.socket.connect(\"127.0.0.1\", ngx.var.port, opts)\n            if not sock then\n                ngx.say(\"connect failed: \" .. err)\n                return\n            end\n\n            local function f()\n                assert(ngx.socket.connect(\"127.0.0.1\", ngx.var.port, opts))\n            end\n\n            local th = ngx.thread.spawn(f)\n            local ok, err = ngx.thread.kill(th)\n            if not ok then\n                ngx.log(ngx.ERR, \"kill thread failed: \", err)\n                return\n            end\n\n            sock:close()\n        }\n    }\n\n    location /t {\n        content_by_lua_block {\n            ngx.location.capture(\"/sub\")\n            -- let pending connect operation resumes first\n            ngx.sleep(0)\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- no_error_log\n[error]\n--- error_log\nlua tcp socket abort queueing\n--- response_body\nok\n\n\n\n=== TEST 53: custom pools in third parameters for unix domain socket\n--- http_config eval\n\"\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n    server {\n        listen unix:$::HtmlDir/nginx.sock;\n        default_type 'text/plain';\n\n        server_tokens off;\n        location /foo {\n            echo foo;\n            more_clear_headers Date;\n        }\n    }\n\"\n--- config\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        content_by_lua '\n            local test = require \"test\"\n            local path = \"$TEST_NGINX_HTML_DIR/nginx.sock\";\n            test.go(path, \"A\")\n            test.go(path, \"B\")\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nfunction go(path, pool)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"unix:\" .. path, nil, {pool = pool})\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\nend\n--- request\nGET /t\n--- response_body\nconnected: 1, reused: 0\nconnected: 1, reused: 0\n--- no_error_log eval\n[\"[error]\",\n\"lua tcp socket keepalive: free connection pool for \",\n\"lua tcp socket get keepalive peer: using connection\"\n]\n--- error_log\nlua tcp socket keepalive create connection pool for key \"A\"\nlua tcp socket keepalive create connection pool for key \"B\"\n\n\n\n=== TEST 54: wrong first argument for setkeepalive\n--- no_http2\n--- quic_max_idle_timeout: 1.2\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n   server_tokens off;\n   location /t {\n        keepalive_timeout 60s;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: keepalive\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\r\\n0\\r\\n\\r\\n\")\n            local data, err = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ok, err = sock:setkeepalive(\"not a number\", \"not a number\")\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n        }\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- error_code:\n--- response_body\n--- error_log eval\nqr/\\Qbad argument #1 to 'setkeepalive' (number expected, got string)\\E/\n--- no_error_log\n[crit]\n--- timeout: 4\n--- curl_error eval\nqr{HTTP/3 stream 0 reset by server}\n\n\n\n=== TEST 55: wrong second argument for setkeepalive\n--- no_http2\n--- quic_max_idle_timeout: 1.2\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n   server_tokens off;\n   location /t {\n        keepalive_timeout 60s;\n\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: keepalive\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"\\r\\n0\\r\\n\\r\\n\")\n            local data, err = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response of \", #data, \" bytes\")\n\n            ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n\n            ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ok, err = sock:setkeepalive(10, \"not a number\")\n            if not ok then\n                ngx.say(\"failed to set reusable: \", err)\n            end\n        }\n    }\n\n    location /foo {\n        echo foo;\n    }\n\n    location /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n--- error_code:\n--- response_body\n--- error_log eval\nqr/\\Qbad argument #2 to 'setkeepalive' (number expected, got string)\\E/\n--- no_error_log\n[crit]\n--- timeout: 4\n--- curl_error eval\nqr{HTTP/3 stream 0 reset by server}\n"
  },
  {
    "path": "t/069-null.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\n#master_on();\n#workers(1);\n#log_level('debug');\n#log_level('warn');\n#worker_connections(1024);\n\nplan tests => repeat_each() * (blocks() * 3 + 1);\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n\nour $LuaCpath = $ENV{LUA_CPATH} ||\n    '/usr/local/openresty-debug/lualib/?.so;/usr/local/openresty/lualib/?.so;;';\n\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: compare ngx.null with cjson.null\n--- http_config eval\n    \"lua_package_cpath '$::LuaCpath';\";\n--- config\n    location /lua {\n        content_by_lua '\n            local cjson = require \"cjson\"\n            ngx.say(cjson.null == ngx.null)\n            ngx.say(cjson.encode(ngx.null))\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ntrue\nnull\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: output ngx.null\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(\"ngx.null: \", ngx.null)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nngx.null: null\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: output ngx.null in a table\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say({\"ngx.null: \", ngx.null})\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nngx.null: null\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: log ngx.null\n--- config\n    location /lua {\n        content_by_lua '\n            print(\"ngx.null: \", ngx.null)\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ndone\n--- error_log\nngx.null: null\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/070-sha1.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: set sha1 hello\n--- config\n    location = /sha1 {\n        content_by_lua 'ngx.say(ngx.encode_base64(ngx.sha1_bin(\"hello\")))';\n    }\n--- request\nGET /sha1\n--- response_body\nqvTGHdzF6KLavt4PO0gs2a6pQ00=\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: set sha1 \"\"\n--- config\n    location = /sha1 {\n        content_by_lua 'ngx.say(ngx.encode_base64(ngx.sha1_bin(\"\")))';\n    }\n--- request\nGET /sha1\n--- response_body\n2jmj7l5rSw0yVb/vlWAYkK/YBwk=\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: set sha1 nil\n--- config\n    location = /sha1 {\n        content_by_lua 'ngx.say(ngx.encode_base64(ngx.sha1_bin(nil)))';\n    }\n--- request\nGET /sha1\n--- response_body\n2jmj7l5rSw0yVb/vlWAYkK/YBwk=\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: set sha1 number\n--- config\n    location = /sha1 {\n        content_by_lua 'ngx.say(ngx.encode_base64(ngx.sha1_bin(512)))';\n    }\n--- request\nGET /sha1\n--- response_body\nzgmxJ9SPg4aKRWReJG07UvS97L4=\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/071-idle-socket.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\nno_long_string();\n#no_diff();\n#log_level 'warn';\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: read events come when socket is idle\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"foofoo\\\\r\\\\n\")\n            local line, err, part = reader()\n            if line then\n                ngx.print(\"read: \", line)\n\n            else\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            local data, err, part = sock:receive(\"*a\")\n            if not data then\n                ngx.say(\"failed to read the 2nd part: \", err)\n            else\n                ngx.say(\"2nd part: [\", data, \"]\")\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /sleep {\n        echo_sleep 0.5;\n        more_clear_headers Date;\n    }\n\n    location /foo {\n        echo -n foofoo;\n        echo_flush;\n        echo_sleep 0.3;\n        echo -n barbar;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: HTTP/1.1 200 OK\\r\nServer: nginx\\r\nContent-Type: text/plain\\r\nTransfer-Encoding: chunked\\r\nConnection: close\\r\n\\r\n6\\r\n2nd part: [6\\r\nbarbar\\r\n0\\r\n\\r\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: read timer cleared in time\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            sock:settimeout(400)\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"flush_all\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local line, err, part = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n\n            else\n                ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent again: \", bytes)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /sleep {\n        echo_sleep 0.5;\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 11\nreceived: OK\nrequest sent again: 11\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: connect timer cleared in time\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            sock:settimeout(300)\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            ngx.location.capture(\"/sleep\")\n\n            local req = \"flush_all\\\\r\\\\n\"\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /sleep {\n        echo_sleep 0.5;\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 11\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: send timer cleared in time\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            sock:settimeout(300)\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"flush_all\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            ngx.location.capture(\"/sleep\")\n\n            local line, err, part = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n\n            else\n                ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                return\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /sleep {\n        echo_sleep 0.5;\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 11\nreceived: OK\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: set keepalive when system socket recv buffer has unread data\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.1\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local reader = sock:receiveuntil(\"foofoo\\\\r\\\\n\")\n            local line, err, part = reader()\n            if line then\n                ngx.print(\"read: \", line)\n\n            else\n                ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n            end\n\n            ngx.location.capture(\"/sleep\")\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set keepalive: \", err)\n            end\n        ';\n    }\n\n    location /sleep {\n        echo_sleep 0.5;\n        more_clear_headers Date;\n    }\n\n    location /foo {\n        echo -n foofoo;\n        echo_flush;\n        echo_sleep 0.3;\n        echo -n barbar;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body_like eval\nqr{connected: 1\nrequest sent: 57\nread: HTTP/1\\.1 200 OK\\r\nServer: nginx\\r\nContent-Type: text/plain\\r\nTransfer-Encoding: chunked\\r\nConnection: close\\r\n\\r\n6\\r\nfailed to set keepalive: (?:unread data in buffer|closed|connection in dubious state)\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: set keepalive when cosocket recv buffer has unread data\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"flush_all\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            local data, err = sock:receive(1)\n            if not data then\n                ngx.say(\"failed to read the 1st byte: \", err)\n                return\n            end\n\n            ngx.say(\"read: \", data)\n\n            local ok, err = sock:setkeepalive()\n            if not ok then\n                ngx.say(\"failed to set keepalive: \", err)\n            end\n        ';\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 11\nread: O\nfailed to set keepalive: unread data in buffer\n}\n--- no_error_log\n[error]\n--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne \"\")\n"
  },
  {
    "path": "t/072-conditional-get.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 2);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: If-Modified-Since true\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.header.last_modified = \"Thu, 10 May 2012 07:50:59 GMT\"\n            ngx.say(\"hello\")\n        ';\n    }\n--- request\nGET /lua\n--- more_headers\nIf-Modified-Since: Thu, 10 May 2012 07:50:59 GMT\n--- response_body\n--- error_code: 304\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: If-Modified-Since true\n--- config\n    location /lua {\n        if_modified_since before;\n        content_by_lua '\n            ngx.header.last_modified = \"Thu, 10 May 2012 07:50:48 GMT\"\n            ngx.say(\"hello\")\n        ';\n    }\n--- request\nGET /lua\n--- more_headers\nIf-Modified-Since: Thu, 10 May 2012 07:50:59 GMT\n--- response_body\n--- error_code: 304\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: If-Unmodified-Since false\n--- config\n    location /lua {\n        #if_modified_since before;\n        content_by_lua '\n            ngx.header.last_modified = \"Thu, 10 May 2012 07:50:48 GMT\"\n            local ok, err = ngx.say(\"hello\")\n            if not ok then\n                ngx.log(ngx.WARN, \"say failed: \", err)\n            end\n        ';\n    }\n--- request\nGET /lua\n--- more_headers\nIf-Unmodified-Since: Thu, 10 May 2012 07:50:47 GMT\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\n\n--- response_body_like: 412 Precondition Failed\n--- error_code: 412\n--- error_log\nsay failed: nginx output filter error\n--- no_error_log\n[error]\n--- skip_eval: 5:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n"
  },
  {
    "path": "t/073-backtrace.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * 51;\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /lua {\n        content_by_lua\n        '   local function bar()\n                return lua_concat(3)\n            end\n            local function foo()\n                bar()\n            end\n            foo()\n        ';\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nattempt to call global 'lua_concat'\n: in function 'bar'\n:5: in function 'foo'\n:7: in main chunk\n\n\n\n=== TEST 2: error(nil)\n--- config\n    location /lua {\n        content_by_lua\n        '   local function bar()\n                error(nil)\n            end\n            local function foo()\n                bar()\n            end\n            foo()\n        ';\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log eval\n[\n'lua entry thread aborted: runtime error: unknown reason',\n'stack traceback:',\n\" in function 'error'\",\n\": in function 'bar'\",\n\":5: in function 'foo'\",\nqr/:7: in main chunk/,\n]\n\n\n\n=== TEST 3: deep backtrace in a single coroutine (more than 15)\n--- config eval\nmy $s = \"\n    location /lua {\n        content_by_lua '\n\";\nmy $prev;\nfor my $i (1..18) {\n    if (!defined $prev) {\n        $s .= \"\n            local function func$i()\n                return error([[blah]])\n            end\";\n    } else {\n        $s .= \"\n            local function func$i()\n                local v = func$prev()\n                return v\n            end\";\n    }\n    $prev = $i;\n}\n$s .= \"\n            func$prev()\n        ';\n    }\n\";\n--- request\nGET /lua\n--- stap2\nprobe process(\"$LIBLUA_PATH\").function(\"lua_concat\") {\n    println(\"lua concat\")\n    //print_ubacktrace()\n}\n--- stap_out2\n--- ignore_response\n--- error_log\n: blah\n: in function 'func1'\n:7: in function 'func2'\n:11: in function 'func3'\n:15: in function 'func4'\n:19: in function 'func5'\n:23: in function 'func6'\n:27: in function 'func7'\n:31: in function 'func8'\n:35: in function 'func9'\n:39: in function 'func10'\n:43: in function 'func11'\n:47: in function 'func12'\n:51: in function 'func13'\n:55: in function 'func14'\n:59: in function 'func15'\n:63: in function 'func16'\n:67: in function 'func17'\n:71: in function 'func18'\n:74: in main chunk\n\n\n\n=== TEST 4: deep backtrace in a single coroutine (more than 22)\n--- config eval\nmy $s = \"\n    location /lua {\n        content_by_lua '\n\";\nmy $prev;\nfor my $i (1..23) {\n    if (!defined $prev) {\n        $s .= \"\n            local function func$i()\n                return error([[blah]])\n            end\";\n    } else {\n        $s .= \"\n            local function func$i()\n                local v = func$prev()\n                return v\n            end\";\n    }\n    $prev = $i;\n}\n$s .= \"\n            func$prev()\n        ';\n    }\n\";\n--- request\nGET /lua\n--- stap2\nprobe process(\"$LIBLUA_PATH\").function(\"lua_concat\") {\n    println(\"lua concat\")\n    //print_ubacktrace()\n}\n--- stap_out2\n--- ignore_response\n--- error_log\n: blah\n: in function 'func1'\n:7: in function 'func2'\n:11: in function 'func3'\n:15: in function 'func4'\n:19: in function 'func5'\n:23: in function 'func6'\n:27: in function 'func7'\n:31: in function 'func8'\n:35: in function 'func9'\n:39: in function 'func10'\n:43: in function 'func11'\n:47: in function 'func12'\n:59: in function 'func15'\n:63: in function 'func16'\n:67: in function 'func17'\n:71: in function 'func18'\n:75: in function 'func19'\n:79: in function 'func20'\n:83: in function 'func21'\n...\n"
  },
  {
    "path": "t/074-prefix-var.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: $prefix\n--- http_config: lua_package_path \"$prefix/html/?.lua;;\";\n--- config\n    location /t {\n        content_by_lua '\n            local foo = require \"foo\"\n            foo.go()\n        ';\n    }\n--- user_files\n>>> foo.lua\nmodule(\"foo\", package.seeall)\n\nfunction go()\n    ngx.say(\"Greetings from module foo.\")\nend\n--- request\nGET /t\n--- response_body\nGreetings from module foo.\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: ${prefix}\n--- http_config: lua_package_path \"${prefix}html/?.lua;;\";\n--- config\n    location /t {\n        content_by_lua '\n            local foo = require \"foo\"\n            foo.go()\n        ';\n    }\n--- user_files\n>>> foo.lua\nmodule(\"foo\", package.seeall)\n\nfunction go()\n    ngx.say(\"Greetings from module foo.\")\nend\n--- request\nGET /t\n--- response_body\nGreetings from module foo.\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/075-logby.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\nlog_level('debug');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 10);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: log_by_lua\n--- config\n    location /lua {\n        echo hello;\n        log_by_lua 'ngx.log(ngx.ERR, \"Hello from log_by_lua: \", ngx.var.uri)';\n    }\n--- request\nGET /lua\n--- response_body\nhello\n--- error_log\nHello from log_by_lua: /lua\n\n\n\n=== TEST 2: log_by_lua_file\n--- config\n    location /lua {\n        echo hello;\n        log_by_lua_file html/a.lua;\n    }\n--- user_files\n>>> a.lua\nngx.log(ngx.ERR, \"Hello from log_by_lua: \", ngx.var.uri)\n--- request\nGET /lua\n--- response_body\nhello\n--- error_log\nHello from log_by_lua: /lua\n\n\n\n=== TEST 3: log_by_lua_file & content_by_lua\n--- config\n    location /lua {\n        set $counter 3;\n        content_by_lua 'ngx.var.counter = ngx.var.counter + 1 ngx.say(ngx.var.counter)';\n        log_by_lua_file html/a.lua;\n    }\n--- user_files\n>>> a.lua\nngx.log(ngx.ERR, \"Hello from log_by_lua: \", ngx.var.counter * 2)\n--- request\nGET /lua\n--- response_body\n4\n--- error_log\nHello from log_by_lua: 8\n\n\n\n=== TEST 4: ngx.ctx available in log_by_lua (already defined)\n--- config\n    location /lua {\n        content_by_lua 'ngx.ctx.counter = 3 ngx.say(ngx.ctx.counter)';\n        log_by_lua 'ngx.log(ngx.ERR, \"ngx.ctx.counter: \", ngx.ctx.counter)';\n    }\n--- request\nGET /lua\n--- response_body\n3\n--- error_log\nngx.ctx.counter: 3\nlua release ngx.ctx\n\n\n\n=== TEST 5: ngx.ctx available in log_by_lua (not defined yet)\n--- config\n    location /lua {\n        echo hello;\n        log_by_lua '\n            ngx.log(ngx.ERR, \"ngx.ctx.counter: \", ngx.ctx.counter)\n            ngx.ctx.counter = \"hello world\"\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nhello\n--- error_log\nngx.ctx.counter: nil\nlua release ngx.ctx\n\n\n\n=== TEST 6: log_by_lua + shared dict\n--- http_config\n    lua_shared_dict foo 100k;\n--- config\n    location /lua {\n        echo hello;\n        log_by_lua '\n            local foo = ngx.shared.foo\n            local key = ngx.var.uri .. ngx.status\n            local newval, err = foo:incr(key, 1)\n            if not newval then\n                if err == \"not found\" then\n                    foo:add(key, 0)\n                    newval, err = foo:incr(key, 1)\n                    if not newval then\n                        ngx.log(ngx.ERR, \"failed to incr \", key, \": \", err)\n                        return\n                    end\n                else\n                    ngx.log(ngx.ERR, \"failed to incr \", key, \": \", err)\n                    return\n                end\n            end\n            print(key, \": \", foo:get(key))\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nhello\n--- error_log eval\nqr{/lua200: [12]}\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: ngx.ctx used in different locations and different ctx (1)\n--- config\n    location /t {\n        echo hello;\n        log_by_lua '\n            ngx.log(ngx.ERR, \"ngx.ctx.counter: \", ngx.ctx.counter)\n        ';\n    }\n\n    location /t2 {\n        content_by_lua '\n            ngx.ctx.counter = 32\n            ngx.say(\"hello\")\n        ';\n    }\n--- request\nGET /t\n--- response_body\nhello\n--- error_log\nngx.ctx.counter: nil\nlua release ngx.ctx\n\n\n\n=== TEST 8: ngx.ctx used in different locations and different ctx (2)\n--- config\n    location /t {\n        echo hello;\n        log_by_lua '\n            ngx.log(ngx.ERR, \"ngx.ctx.counter: \", ngx.ctx.counter)\n        ';\n    }\n\n    location /t2 {\n        content_by_lua '\n            ngx.ctx.counter = 32\n            ngx.say(ngx.ctx.counter)\n        ';\n    }\n--- request\nGET /t2\n--- response_body\n32\n--- error_log\nlua release ngx.ctx\n\n\n\n=== TEST 9: lua error (string)\n--- config\n    location /lua {\n        log_by_lua 'error(\"Bad\")';\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log eval\nqr/failed to run log_by_lua\\*: log_by_lua\\(nginx\\.conf:\\d+\\):1: Bad/\n\n\n\n=== TEST 10: lua error (nil)\n--- config\n    location /lua {\n        log_by_lua 'error(nil)';\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nfailed to run log_by_lua*: unknown reason\n\n\n\n=== TEST 11: globals sharing\n--- config\n    location /lua {\n        echo ok;\n        log_by_lua '\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.INFO, \"old foo: \", foo)\n                foo = foo + 1\n            end\n            ngx.log(ngx.WARN, \"foo = \", foo)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- grep_error_log eval: qr/old foo: \\d+/\n--- grep_error_log_out eval\n[\"\", \"old foo: 1\\n\"]\n\n\n\n=== TEST 12: no ngx.print\n--- config\n    location /lua {\n        log_by_lua \"ngx.print(32) return 1\";\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 13: no ngx.say\n--- config\n    location /lua {\n        log_by_lua \"ngx.say(32) return 1\";\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 14: no ngx.flush\n--- config\n    location /lua {\n        log_by_lua \"ngx.flush()\";\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 15: no ngx.eof\n--- config\n    location /lua {\n        log_by_lua \"ngx.eof()\";\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 16: no ngx.send_headers\n--- config\n    location /lua {\n        log_by_lua \"ngx.send_headers()\";\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 17: no ngx.location.capture\n--- config\n    location /lua {\n        log_by_lua 'ngx.location.capture(\"/sub\")';\n        echo ok;\n    }\n\n    location /sub {\n        echo sub;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 18: no ngx.location.capture_multi\n--- config\n    location /lua {\n        log_by_lua 'ngx.location.capture_multi{{\"/sub\"}}';\n        echo ok;\n    }\n\n    location /sub {\n        echo sub;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 19: no ngx.exit\n--- config\n    location /lua {\n        log_by_lua 'ngx.exit(0)';\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 20: no ngx.redirect\n--- config\n    location /lua {\n        log_by_lua 'ngx.redirect(\"/blah\")';\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 21: no ngx.exec\n--- config\n    location /lua {\n        log_by_lua 'ngx.exec(\"/blah\")';\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 22: no ngx.req.set_uri(uri, true)\n--- config\n    location /lua {\n        log_by_lua 'ngx.req.set_uri(\"/blah\", true)';\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 23: ngx.req.set_uri(uri) exists\n--- config\n    location /lua {\n        log_by_lua 'ngx.req.set_uri(\"/blah\") print(\"log_by_lua: uri: \", ngx.var.uri)';\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nlog_by_lua: uri: /blah\n\n\n\n=== TEST 24: no ngx.req.read_body()\n--- config\n    location /lua {\n        log_by_lua 'ngx.req.read_body()';\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 25: no ngx.req.socket()\n--- config\n    location /lua {\n        log_by_lua 'return ngx.req.socket()';\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log eval\nmy $err_log;\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $err_log = \"http v3 not supported yet\";\n} else {\n    $err_log = \"API disabled in the context of log_by_lua*\";\n}\n\n$err_log;\n\n\n\n=== TEST 26: no ngx.socket.tcp()\n--- config\n    location /lua {\n        log_by_lua 'return ngx.socket.tcp()';\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 27: no ngx.socket.connect()\n--- config\n    location /lua {\n        log_by_lua 'return ngx.socket.connect(\"127.0.0.1\", 80)';\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 28: backtrace\n--- config\n    location /t {\n        echo ok;\n        log_by_lua '\n            local bar\n            local function foo()\n                bar()\n            end\n\n            function bar()\n                error(\"something bad happened\")\n            end\n\n            foo()\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nok\n--- error_log\nsomething bad happened\nstack traceback:\nin function 'error'\nin function 'bar'\nin function 'foo'\n\n\n\n=== TEST 29: Lua file does not exist\n--- config\n    location /lua {\n        echo ok;\n        log_by_lua_file html/test2.lua;\n    }\n--- user_files\n>>> test.lua\nv = ngx.var[\"request_uri\"]\nngx.print(\"request_uri: \", v, \"\\n\")\n--- request\nGET /lua?a=1&b=2\n--- response_body\nok\n--- error_log eval\nqr/failed to load external Lua file \".*?test2\\.lua\": cannot open .*? No such file or directory/\n\n\n\n=== TEST 30: log_by_lua runs before access logging (github issue #254)\n--- config\n    location /lua {\n        echo ok;\n        access_log logs/foo.log;\n        log_by_lua 'print(\"hello\")';\n    }\n--- request\nGET /lua\n--- stap\nF(ngx_http_log_handler) {\n    println(\"log handler\")\n}\nF(ngx_http_lua_log_handler) {\n    println(\"lua log handler\")\n}\n--- stap_out\nlua log handler\nlog handler\n\n--- response_body\nok\n--- no_error_log\n[error]\n\n\n\n=== TEST 31: reading ngx.header.HEADER in log_by_lua\n--- config\n    location /lua {\n        echo ok;\n        log_by_lua 'ngx.log(ngx.WARN, \"content-type: \", ngx.header.content_type)';\n    }\n--- request\nGET /lua\n\n--- response_body\nok\n--- error_log eval\nqr{log_by_lua\\(nginx\\.conf:\\d+\\):1: content-type: text/plain}\n\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/076-no-postpone.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3 + 1);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: rewrite no postpone on\n--- http_config\n    rewrite_by_lua_no_postpone on;\n--- config\n    set $foo '';\n    location /t {\n        rewrite_by_lua '\n            ngx.var.foo = 1\n        ';\n        if ($foo = 1) {\n            echo \"foo: $foo\";\n        }\n        echo \"no foo: $foo\";\n    }\n--- request\nGET /t\n--- response_body\nfoo: 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: rewrite no postpone explicitly off\n--- http_config\n    rewrite_by_lua_no_postpone off;\n--- config\n    set $foo '';\n    location /t {\n        rewrite_by_lua '\n            ngx.var.foo = 1\n        ';\n        if ($foo = 1) {\n            echo \"foo: $foo\";\n        }\n        echo \"no foo: $foo\";\n    }\n--- request\nGET /t\n--- response_body\nno foo: 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: rewrite no postpone off by default\n--- config\n    set $foo '';\n    location /t {\n        rewrite_by_lua '\n            ngx.var.foo = 1\n        ';\n        if ($foo = 1) {\n            echo \"foo: $foo\";\n        }\n        echo \"no foo: $foo\";\n    }\n--- request\nGET /t\n--- response_body\nno foo: 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: access no postpone on\n--- http_config\n    access_by_lua_no_postpone on;\n--- config\n    location /t {\n        access_by_lua '\n            ngx.redirect(\"http://www.taobao.com/foo\")\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n        deny all;\n    }\n--- request\nGET /t\n--- response_headers\nLocation: http://www.taobao.com/foo\n--- response_body_like: 302 Found\n--- error_code: 302\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: access no postpone explicitly off\n--- http_config\n    access_by_lua_no_postpone off;\n--- config\n    location /t {\n        access_by_lua '\n            ngx.redirect(\"http://www.taobao.com/foo\")\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n        deny all;\n    }\n--- request\nGET /t\n--- response_body_like: 403 Forbidden\n--- error_code: 403\n--- error_log\naccess forbidden by rule\n\n\n\n=== TEST 6: access no postpone off by default\n--- config\n    location /t {\n        access_by_lua '\n            ngx.redirect(\"http://www.taobao.com/foo\")\n            ngx.say(\"hi\")\n        ';\n        content_by_lua 'return';\n        deny all;\n    }\n--- request\nGET /t\n--- response_body_like: 403 Forbidden\n--- error_code: 403\n--- error_log\naccess forbidden by rule\n"
  },
  {
    "path": "t/077-sleep.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\nlog_level('debug');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 71;\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sleep 0.5 - content\n--- config\n    location /test {\n        content_by_lua '\n            ngx.update_time()\n            local before = ngx.now()\n            ngx.sleep(0.5)\n            local now = ngx.now()\n            ngx.say(now - before)\n        ';\n    }\n--- request\nGET /test\n--- response_body_like chop\n^0\\.(?:4[5-9]\\d*|5[0-5]\\d*|5)$\n--- error_log\nlua ready to sleep for\nlua sleep timer expired: \"/test?\"\nlua sleep timer expired: \"/test?\"\n\n\n\n=== TEST 2: sleep a - content\n--- config\n    location /test {\n        content_by_lua '\n            ngx.update_time()\n            local before = ngx.now()\n            ngx.sleep(\"a\")\n            local now = ngx.now()\n            ngx.say(now - before)\n        ';\n    }\n--- request\nGET /test\n--- error_code: 500\n--- response_body_like: 500 Internal Server Error\n--- error_log\nbad argument #1 to 'sleep'\n\n\n\n=== TEST 3: sleep 0.5 in subrequest - content\n--- config\n    location /test {\n        content_by_lua '\n            ngx.update_time()\n            local before = ngx.now()\n            ngx.location.capture(\"/sleep\")\n            local now = ngx.now()\n            local delay = now - before\n            ngx.say(delay)\n        ';\n    }\n    location /sleep {\n        content_by_lua 'ngx.sleep(0.5)';\n    }\n--- request\nGET /test\n--- response_body_like chop\n^0\\.(?:4[5-9]\\d*|5[0-9]\\d*|5)$\n--- error_log\nlua ready to sleep for\nlua sleep timer expired: \"/sleep?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: sleep a in subrequest with bad argument\n--- config\n    location /test {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sleep\");\n        ';\n    }\n    location /sleep {\n        content_by_lua 'ngx.sleep(\"a\")';\n    }\n--- request\nGET /test\n--- response_body_like:\n--- error_log\nbad argument #1 to 'sleep'\n\n\n\n=== TEST 5: sleep 0.33 - multi-times in content\n--- quic_max_idle_timeout: 1.1\n--- config\n    location /test {\n        content_by_lua '\n            ngx.update_time()\n            local start = ngx.now()\n            ngx.sleep(0.33)\n            ngx.sleep(0.33)\n            ngx.sleep(0.33)\n            ngx.say(ngx.now() - start)\n        ';\n    }\n--- request\nGET /test\n--- response_body_like chop\n^(?:0\\.9\\d*|1\\.[0-2]\\d*|1)$\n--- error_log\nlua ready to sleep for\nlua sleep timer expired: \"/test?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: sleep 0.5 - interleaved by ngx.say() - ended by ngx.sleep\n--- quic_max_idle_timeout: 2.2\n--- config\n    location /test {\n        content_by_lua '\n            ngx.send_headers()\n            -- ngx.location.capture(\"/sleep\")\n            ngx.sleep(1)\n            ngx.say(\"blah\")\n            ngx.sleep(1)\n            -- ngx.location.capture(\"/sleep\")\n        ';\n    }\n    location = /sleep {\n        echo_sleep 0.1;\n    }\n--- request\nGET /test\n--- response_body\nblah\n--- error_log\nlua ready to sleep\nlua sleep timer expired: \"/test?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: sleep 0.5 - interleaved by ngx.say() - not ended by ngx.sleep\n--- quic_max_idle_timeout: 0.9\n--- config\n    location /test {\n        content_by_lua '\n            ngx.send_headers()\n            -- ngx.location.capture(\"/sleep\")\n            ngx.sleep(0.3)\n            ngx.say(\"blah\")\n            ngx.sleep(0.5)\n            -- ngx.location.capture(\"/sleep\")\n            ngx.say(\"hiya\")\n        ';\n    }\n    location = /sleep {\n        echo_sleep 0.1;\n    }\n--- request\nGET /test\n--- response_body\nblah\nhiya\n--- error_log\nlua ready to sleep for\nlua sleep timer expired: \"/test?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: ngx.location.capture before and after ngx.sleep\n--- config\n    location /test {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n\n            ngx.sleep(0.1)\n\n            res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n        ';\n    }\n    location = /hello {\n        echo hello world;\n    }\n    location = /sub {\n        proxy_pass http://127.0.0.1:$server_port/hello;\n    }\n--- request\nGET /test\n--- response_body\nhello world\nhello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: sleep 0\n--- config\n    location /test {\n        content_by_lua '\n            ngx.update_time()\n            local before = ngx.now()\n            ngx.sleep(0)\n            local now = ngx.now()\n            ngx.say(\"elapsed: \", now - before)\n        ';\n    }\n--- request\nGET /test\n--- response_body_like chop\nelapsed: 0\n--- error_log\nlua ready to sleep for\nlua sleep timer expired: \"/test?\"\nlua sleep timer expired: \"/test?\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: ngx.sleep unavailable in log_by_lua\n--- config\n    location /t {\n        echo hello;\n        log_by_lua '\n            ngx.sleep(0.1)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nhello\n--- wait: 0.1\n--- error_log\nAPI disabled in the context of log_by_lua*\n\n\n\n=== TEST 11: ngx.sleep() fails to yield (xpcall err handler)\n--- config\n    location = /t {\n        content_by_lua '\n            local function f()\n                return error(1)\n            end\n            local function err()\n                ngx.sleep(0.001)\n            end\n            xpcall(f, err)\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nok\n--- error_log\nlua clean up the timer for pending ngx.sleep\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: ngx.sleep() fails to yield (require)\n--- http_config\n    lua_package_path \"$prefix/html/?.lua;;\";\n--- config\n    location = /t {\n        content_by_lua '\n            package.loaded[\"foosleep\"] = nil\n            require \"foosleep\";\n        ';\n    }\n--- request\n    GET /t\n--- user_files\n>>> foosleep.lua\nngx.sleep(0.001)\n\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- wait: 0.2\n--- error_log eval\n[\n\"lua clean up the timer for pending ngx.sleep\",\nqr{runtime error: attempt to yield across (?:metamethod/)?C-call boundary},\n]\n\n\n\n=== TEST 13: sleep coctx handler did not get called in ngx.exit().\n--- config\n    location /t {\n         content_by_lua \"\n            local function sleep(t)\n                --- nginx return reply to client without waiting\n                ngx.sleep(t)\n            end\n\n            local function wait()\n                 --- worker would crash afterwards\n                 xpcall(function () error(1) end, function() return sleep(0.001) end)\n                 --- ngx.exit was required to crash worker\n                 ngx.exit(200)\n            end\n\n            wait()\n         \";\n    }\n--- request\n    GET /t\n\n--- wait: 0.1\n--- response_body\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 14: sleep coctx handler did not get called in ngx.exec().\n--- config\n    location /t {\n         content_by_lua '\n            local function sleep(t)\n                --- nginx return reply to client without waiting\n                ngx.sleep(t)\n            end\n\n            local function wait()\n                 --- worker would crash afterwards\n                 xpcall(function () error(1) end, function() return sleep(0.001) end)\n                 --- ngx.exit was required to crash worker\n                 ngx.exec(\"/dummy\")\n            end\n\n            wait()\n         ';\n    }\n\n    location /dummy {\n        echo ok;\n    }\n--- request\n    GET /t\n\n--- wait: 0.1\n--- response_body\nok\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 15: sleep coctx handler did not get called in ngx.req.set_uri(uri, true).\n--- config\n    location /t {\n         rewrite_by_lua '\n            local function sleep(t)\n                --- nginx return reply to client without waiting\n                ngx.sleep(t)\n            end\n\n            local function wait()\n                 --- worker would crash afterwards\n                 xpcall(function () error(1) end, function() return sleep(0.001) end)\n                 --- ngx.exit was required to crash worker\n                 ngx.req.set_uri(\"/dummy\", true)\n            end\n\n            wait()\n         ';\n    }\n\n    location /dummy {\n        echo ok;\n    }\n--- request\n    GET /t\n\n--- wait: 0.1\n--- response_body\nok\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 16: sleep 0\n--- config\n    location /t {\n        content_by_lua_block {\n            local function f (n)\n                print(\"f begin \", n)\n                ngx.sleep(0)\n                print(\"f middle \", n)\n                ngx.sleep(0)\n                print(\"f end \", n)\n                ngx.sleep(0)\n            end\n\n            for i = 1, 3 do\n                assert(ngx.thread.spawn(f, i))\n            end\n\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/\\bf (?:begin|middle|end)\\b|\\bworker cycle$|\\be?poll timer: \\d+$/\n--- grep_error_log_out eval\nqr/f begin\nf begin\nf begin\nworker cycle\ne?poll timer: 0\nf middle\nf middle\nf middle\nworker cycle\ne?poll timer: 0\nf end\nf end\nf end\nworker cycle\ne?poll timer: 0\n/\n\n\n\n=== TEST 17: sleep short times less than 1ms\n--- config\n    location /t {\n        content_by_lua_block {\n            local delay = 0.0005\n\n            local function f (n)\n                print(\"f begin \", n)\n                ngx.sleep(delay)\n                print(\"f middle \", n)\n                ngx.sleep(delay)\n                print(\"f end \", n)\n                ngx.sleep(delay)\n            end\n\n            for i = 1, 3 do\n                assert(ngx.thread.spawn(f, i))\n            end\n\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/\\bf (?:begin|middle|end)\\b|\\bworker cycle$|\\be?poll timer: \\d+$/\n--- grep_error_log_out eval\nqr/f begin\nf begin\nf begin\nworker cycle\ne?poll timer: 0\nf middle\nf middle\nf middle\nworker cycle\ne?poll timer: 0\nf end\nf end\nf end\nworker cycle\ne?poll timer: 0\n/\n"
  },
  {
    "path": "t/078-hup-vars.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_CHECK_LEAK}) {\n        $SkipReason = \"unavailable for the hup tests\";\n\n    } else {\n        $ENV{TEST_NGINX_USE_HUP} = 1;\n        undef $ENV{TEST_NGINX_USE_STAP};\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('debug');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (3 * blocks());\n\n#no_diff();\n#no_long_string();\nno_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: nginx variable hup bug (step 1)\nhttp://mailman.nginx.org/pipermail/nginx-devel/2012-May/002223.html\n--- config\n    location /t {\n        set $vv $http_host;\n        set_by_lua $i 'return ngx.var.http_host';\n        echo $i;\n    }\n--- request\nGET /t\n--- response_body\nlocalhost\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: nginx variable hup bug (step 2)\nhttp://mailman.nginx.org/pipermail/nginx-devel/2012-May/002223.html\n--- config\n    location /t {\n        #set $vv $http_host;\n        set_by_lua $i 'return ngx.var.http_host';\n        echo $i;\n    }\n--- request\nGET /t\n--- response_body\nlocalhost\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/079-unused-directives.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\nlog_level('debug');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 110;\n\n#no_diff();\n#no_long_string();\n#no_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: rewrite_by_lua unused\n--- config\n    location /t {\n        set_by_lua $i 'return 32';\n        #rewrite_by_lua return;\n        echo $i;\n    }\n--- request\nGET /t\n--- response_body\n32\n--- no_error_log\nlua capture header filter, uri \"/t\"\nlua capture body filter, uri \"/t\"\nlua rewrite handler, uri:\"/t\"\nlua access handler, uri:\"/t\"\nlua content handler, uri:\"/t\"\nlua header filter for user lua code, uri \"/t\"\nlua body filter for user lua code, uri \"/t\"\nlua log handler, uri:\"/t\"\n[error]\n\n\n\n=== TEST 2: rewrite_by_lua used\n--- config\n    location /t {\n        rewrite_by_lua return;\n        echo hello;\n    }\n--- request\nGET /t\n--- response_body\nhello\n--- error_log\nlua rewrite handler, uri:\"/t\"\nlua capture header filter, uri \"/t\"\nlua capture body filter, uri \"/t\"\n--- no_error_log\nlua access handler, uri:\"/t\"\nlua content handler, uri:\"/t\"\nlua header filter for user lua code, uri \"/t\"\nlua body filter for user lua code, uri \"/t\"\nlua log handler, uri:\"/t\"\n[error]\n--- log_level: debug\n\n\n\n=== TEST 3: access_by_lua used\n--- config\n    location /t {\n        access_by_lua return;\n        echo hello;\n    }\n--- request\nGET /t\n--- response_body\nhello\n--- error_log\nlua access handler, uri:\"/t\" c:1\nlua capture body filter, uri \"/t\"\nlua capture header filter, uri \"/t\"\n--- no_error_log\nlua rewrite handler, uri:\"/t\"\nlua content handler, uri:\"/t\"\nlua header filter for user lua code, uri \"/t\"\nlua body filter for user lua code, uri \"/t\"\nlua log handler, uri:\"/t\"\n[error]\n\n\n\n=== TEST 4: content_by_lua used\n--- config\n    location /t {\n        content_by_lua 'ngx.say(\"hello\")';\n    }\n--- request\nGET /t\n--- response_body\nhello\n--- error_log\nlua content handler, uri:\"/t\" c:1\nlua capture body filter, uri \"/t\"\nlua capture header filter, uri \"/t\"\n--- no_error_log\nlua access handler, uri:\"/t\"\nlua rewrite handler, uri:\"/t\"\nlua header filter for user lua code, uri \"/t\"\nlua body filter for user lua code, uri \"/t\"\nlua log handler, uri:\"/t\"\n[error]\n\n\n\n=== TEST 5: header_filter_by_lua\n--- config\n    location /t {\n        echo hello;\n        header_filter_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nhello\n--- error_log\nlua header filter for user lua code, uri \"/t\"\n--- no_error_log\nlua capture header filter, uri \"/t\"\nlua content handler, uri:\"/t\"\nlua access handler, uri:\"/t\"\nlua rewrite handler, uri:\"/t\"\nlua capture body filter, uri \"/t\"\nlua log handler, uri:\"/t\"\nlua body filter for user lua code, uri \"/t\"\n[error]\n\n\n\n=== TEST 6: log_by_lua\n--- config\n    location /t {\n        echo hello;\n        log_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nhello\n--- error_log\nlua log handler, uri:\"/t\"\n--- no_error_log\nlua header filter for user lua code, uri \"/t\"\nlua capture header filter, uri \"/t\"\nlua content handler, uri:\"/t\"\nlua access handler, uri:\"/t\"\nlua rewrite handler, uri:\"/t\"\nlua capture body filter, uri \"/t\"\nlua body filter for user lua code, uri \"/t\"\n[error]\n\n\n\n=== TEST 7: body_filter_by_lua\n--- config\n    location /t {\n        echo hello;\n        body_filter_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nhello\n--- error_log\nlua header filter for user lua code, uri \"/t\"\nlua body filter for user lua code, uri \"/t\"\n--- no_error_log\nlua capture header filter, uri \"/t\"\nlua content handler, uri:\"/t\"\nlua access handler, uri:\"/t\"\nlua rewrite handler, uri:\"/t\"\nlua capture body filter, uri \"/t\"\nlua log handler, uri:\"/t\"\n[error]\n\n\n\n=== TEST 8: header_filter_by_lua_file\n--- config\n    location /t {\n        echo hello;\n        header_filter_by_lua_file html/a.lua;\n    }\n--- user_files\n>>> a.lua\nreturn\n--- request\nGET /t\n--- response_body\nhello\n--- error_log\nlua header filter for user lua code, uri \"/t\"\n--- no_error_log\nlua capture header filter, uri \"/t\"\nlua content handler, uri:\"/t\"\nlua access handler, uri:\"/t\"\nlua rewrite handler, uri:\"/t\"\nlua capture body filter, uri \"/t\"\nlua log handler, uri:\"/t\"\nlua body filter for user lua code, uri \"/t\"\n[error]\n\n\n\n=== TEST 9: log_by_lua\n--- config\n    location /t {\n        echo hello;\n        log_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nhello\n--- error_log\nlua log handler, uri:\"/t\"\n--- no_error_log\nlua header filter for user lua code, uri \"/t\"\nlua capture header filter, uri \"/t\"\nlua content handler, uri:\"/t\"\nlua access handler, uri:\"/t\"\nlua rewrite handler, uri:\"/t\"\nlua capture body filter, uri \"/t\"\nlua body filter for user lua code, uri \"/t\"\n[error]\n\n\n\n=== TEST 10: body_filter_by_lua\n--- config\n    location /t {\n        echo hello;\n        body_filter_by_lua return;\n    }\n--- request\nGET /t\n--- response_body\nhello\n--- error_log\nlua header filter for user lua code, uri \"/t\"\nlua body filter for user lua code, uri \"/t\"\n--- no_error_log\nlua capture header filter, uri \"/t\"\nlua content handler, uri:\"/t\"\nlua access handler, uri:\"/t\"\nlua rewrite handler, uri:\"/t\"\nlua capture body filter, uri \"/t\"\nlua log handler, uri:\"/t\"\n[error]\n\n\n\n=== TEST 11: header_filter_by_lua with multiple http blocks (github issue #294)\nThis test case won't run with nginx 1.9.3+ since duplicate http {} blocks\nhave been prohibited since then.\n--- SKIP\n--- config\n    location = /t {\n        echo ok;\n        header_filter_by_lua '\n            ngx.status = 201\n            ngx.header.Foo = \"foo\"\n        ';\n\n    }\n--- post_main_config\n    http {\n    }\n--- request\nGET /t\n--- response_headers\nFoo: foo\n--- response_body\nok\n--- error_code: 201\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: body_filter_by_lua in multiple http blocks (github issue #294)\nThis test case won't run with nginx 1.9.3+ since duplicate http {} blocks\nhave been prohibited since then.\n--- SKIP\n--- config\n    location = /t {\n        echo -n ok;\n        body_filter_by_lua '\n            if ngx.arg[2] then\n                ngx.arg[1] = ngx.arg[1] .. \"ay\\\\n\"\n            end\n        ';\n\n    }\n--- post_main_config\n    http {\n    }\n--- request\nGET /t\n--- response_body\nokay\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: capture filter with multiple http blocks (github issue #294)\nThis test case won't run with nginx 1.9.3+ since duplicate http {} blocks\nhave been prohibited since then.\n--- SKIP\n--- config\n    location = /t {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.say(\"sub: \", res.body)\n        ';\n    }\n\n    location = /sub {\n        echo -n sub;\n    }\n--- post_main_config\n    http {\n    }\n--- request\nGET /t\n--- response_body\nsub: sub\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/080-hup-shdict.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_CHECK_LEAK}) {\n        $SkipReason = \"unavailable for the hup tests\";\n\n    } else {\n        $ENV{TEST_NGINX_USE_HUP} = 1;\n        undef $ENV{TEST_NGINX_USE_STAP};\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\nno_long_string();\n#master_on();\n#workers(2);\n\nno_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: initialize the fields in shdict\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            dogs:set(\"bah\", 10502)\n            local val = dogs:get(\"foo\")\n            ngx.say(val, \" \", type(val))\n            val = dogs:get(\"bah\")\n            ngx.say(val, \" \", type(val))\n        ';\n    }\n--- request\nGET /test\n--- response_body\n32 number\n10502 number\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: retrieve the fields in shdict after HUP reload\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            -- dogs:set(\"foo\", 32)\n            -- dogs:set(\"bah\", 10502)\n\n            local val = dogs:get(\"foo\")\n            ngx.say(val, \" \", type(val))\n            val = dogs:get(\"bah\")\n            ngx.say(val, \" \", type(val))\n        ';\n    }\n--- request\nGET /test\n--- response_body\n32 number\n10502 number\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/081-bytecode.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: bytecode (\"ngx.say('hello');\")\n--- config\n    root html;\n    location /save_call {\n        content_by_lua '\n            ngx.req.read_body();\n            local b = ngx.req.get_body_data();\n            local f = io.open(ngx.var.realpath_root..\"/test.lua\", \"w\");\n            -- luajit bytecode: sub(149,-1), lua bytecode: sub(1,147)\n            if jit then\n                if not string.find(jit.version, \"LuaJIT 2.0\") then\n                    ngx.say(\"test skipped\")\n                    return\n                end\n                f:write(string.sub(b, 149));\n            else\n                f:write(string.sub(b, 1, 147));\n            end\n            f:close();\n            local res = ngx.location.capture(\"/call\");\n            ngx.print(res.body)\n        ';\n    }\n    location /call {\n        content_by_lua_file $realpath_root/test.lua;\n    }\n--- request eval\n\"POST /save_call\n\\x1b\\x4c\\x75\\x61\\x51\\x00\\x01\\x04\\x08\\x04\\x08\\x00\\x0a\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x74\\x65\\x73\\x74\\x2e\\x6c\\x75\\x61\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x02\\x05\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x40\\x40\\x00\\x41\\x80\\x00\\x00\\x1c\\x40\\x00\\x01\\x1e\\x00\\x80\\x00\\x03\\x00\\x00\\x00\\x04\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x6e\\x67\\x78\\x00\\x04\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x73\\x61\\x79\\x00\\x04\\x06\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x68\\x65\\x6c\\x6c\\x6f\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\n\\x1b\\x4c\\x4a\\x01\\x02\\x29\\x02\\x00\\x02\\x00\\x03\\x00\\x05\\x34\\x00\\x00\\x00\\x37\\x00\\x01\\x00\\x25\\x01\\x02\\x00\\x3e\\x00\\x02\\x01\\x47\\x00\\x01\\x00\\x0a\\x68\\x65\\x6c\\x6c\\x6f\\x08\\x73\\x61\\x79\\x08\\x6e\\x67\\x78\\x00\"\n--- response_body_like chop\n^(?:hello|test skipped)$\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: luajit load lua bytecode or lua load luajit bytecode\n--- config\n    root html;\n    location /save_call {\n        content_by_lua '\n            ngx.req.read_body();\n            local b = ngx.req.get_body_data();\n            local f = io.open(ngx.var.realpath_root..\"/test.lua\", \"w\");\n            -- luajit bytecode: sub(149,-1), lua bytecode: sub(1,147)\n            if not package.loaded[\"jit\"] then\n                f:write(string.sub(b, 149));\n            else\n                f:write(string.sub(b, 1, 147));\n            end\n            f:close();\n            local res = ngx.location.capture(\"/call\");\n            if res.status == 200 then\n                ngx.print(res.body)\n            else\n                ngx.say(\"error\")\n            end\n        ';\n    }\n    location /call {\n        content_by_lua_file $realpath_root/test.lua;\n    }\n--- request eval\n\"POST /save_call\n\\x1b\\x4c\\x75\\x61\\x51\\x00\\x01\\x04\\x08\\x04\\x08\\x00\\x0a\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x74\\x65\\x73\\x74\\x2e\\x6c\\x75\\x61\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x02\\x05\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x40\\x40\\x00\\x41\\x80\\x00\\x00\\x1c\\x40\\x00\\x01\\x1e\\x00\\x80\\x00\\x03\\x00\\x00\\x00\\x04\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x6e\\x67\\x78\\x00\\x04\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x73\\x61\\x79\\x00\\x04\\x06\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x68\\x65\\x6c\\x6c\\x6f\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\n\\x1b\\x4c\\x4a\\x01\\x02\\x29\\x02\\x00\\x02\\x00\\x03\\x00\\x05\\x34\\x00\\x00\\x00\\x37\\x00\\x01\\x00\\x25\\x01\\x02\\x00\\x3e\\x00\\x02\\x01\\x47\\x00\\x01\\x00\\x0a\\x68\\x65\\x6c\\x6c\\x6f\\x08\\x73\\x61\\x79\\x08\\x6e\\x67\\x78\\x00\"\n--- response_body\nerror\n--- error_log eval\nqr/failed to load external Lua file \".*?test\\.lua\": .* cannot load incompatible bytecode/\n\n\n\n=== TEST 3: unknown bytecode version\n--- config\n    root html;\n    location /save_call {\n        content_by_lua '\n            ngx.req.read_body();\n            local b = ngx.req.get_body_data();\n            local f = io.open(ngx.var.realpath_root..\"/test.lua\", \"w\");\n            -- luajit bytecode: sub(149,-1), lua bytecode: sub(1,147)\n            if package.loaded[\"jit\"] then\n                f:write(string.sub(b, 149));\n            else\n                f:write(string.sub(b, 1, 147));\n            end\n            f:close();\n            local res = ngx.location.capture(\"/call\");\n            if res.status == 200 then\n                ngx.print(res.body)\n            else\n                ngx.say(\"error\")\n            end\n        ';\n    }\n    location /call {\n        content_by_lua_file $realpath_root/test.lua;\n    }\n--- request eval\n\"POST /save_call\n\\x1b\\x4c\\x75\\x61\\x52\\x00\\x01\\x04\\x08\\x04\\x08\\x00\\x0a\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x74\\x65\\x73\\x74\\x2e\\x6c\\x75\\x61\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x02\\x05\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x40\\x40\\x00\\x41\\x80\\x00\\x00\\x1c\\x40\\x00\\x01\\x1e\\x00\\x80\\x00\\x03\\x00\\x00\\x00\\x04\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x6e\\x67\\x78\\x00\\x04\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x73\\x61\\x79\\x00\\x04\\x06\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x68\\x65\\x6c\\x6c\\x6f\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\n\\x1b\\x4c\\x4a\\x80\\x02\\x29\\x02\\x00\\x02\\x00\\x03\\x00\\x05\\x34\\x00\\x00\\x00\\x37\\x00\\x01\\x00\\x25\\x01\\x02\\x00\\x3e\\x00\\x02\\x01\\x47\\x00\\x01\\x00\\x0a\\x68\\x65\\x6c\\x6c\\x6f\\x08\\x73\\x61\\x79\\x08\\x6e\\x67\\x78\\x00\"\n--- response_body\nerror\n--- error_log\ncannot load incompatible bytecode\n\n\n\n=== TEST 4: bytecode (big endian)\n--- config\n    root html;\n    location /save_call {\n        content_by_lua '\n            ngx.req.read_body();\n            local b = ngx.req.get_body_data();\n            local f = io.open(ngx.var.realpath_root..\"/test.lua\", \"w\");\n            -- luajit bytecode: sub(149,-1), lua bytecode: sub(1,147)\n            local do_jit\n            if jit then\n                if not string.find(jit.version, \"LuaJIT 2.0\") then\n                    ngx.say(\"test skipped\")\n                    return\n                end\n\n                do_jit = true; f:write(string.sub(b, 149));\n            else\n                f:write(string.sub(b, 1, 147));\n            end\n            f:close(); res = ngx.location.capture(\"/call\");\n            if do_jit and res.status == 200 then\n                ngx.say(\"ok\")\n            elseif not do_jit and res.status == 500 then\n                ngx.say(\"ok\")\n            else\n                ngx.say(\"error\")\n            end\n        ';\n    }\n    location /call {\n        content_by_lua_file $realpath_root/test.lua;\n    }\n--- request eval\n\"POST /save_call\n\\x1b\\x4c\\x75\\x61\\x51\\x00\\x00\\x04\\x08\\x04\\x08\\x00\\x0a\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x74\\x65\\x73\\x74\\x2e\\x6c\\x75\\x61\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x02\\x05\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x40\\x40\\x00\\x41\\x80\\x00\\x00\\x1c\\x40\\x00\\x01\\x1e\\x00\\x80\\x00\\x03\\x00\\x00\\x00\\x04\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x6e\\x67\\x78\\x00\\x04\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x73\\x61\\x79\\x00\\x04\\x06\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x68\\x65\\x6c\\x6c\\x6f\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\n\\x1b\\x4c\\x4a\\x01\\x03\\x29\\x02\\x00\\x02\\x00\\x03\\x00\\x05\\x00\\x00\\x00\\x34\\x00\\x01\\x00\\x37\\x00\\x02\\x01\\x25\\x01\\x02\\x00\\x3e\\x00\\x01\\x00\\x47\\x0a\\x68\\x65\\x6c\\x6c\\x6f\\x08\\x73\\x61\\x79\\x08\\x6e\\x67\\x78\\x00\"\n--- response_body_like chop\n^(?:ok|test skipped)$\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: good header but bad body\n--- config\n    root html;\n    location /save_call {\n        content_by_lua '\n            ngx.req.read_body();\n            local b = ngx.req.get_body_data();\n            local f = io.open(ngx.var.realpath_root..\"/test.lua\", \"w\");\n            -- luajit bytecode: sub(149,-1), lua bytecode: sub(1,147)\n            local jit;\n            if package.loaded[\"jit\"] then\n                jit = true;\n                f:write(string.sub(b, 149));\n            else\n                f:write(string.sub(b, 1, 147));\n            end\n            if not jit then\n                f:close(); res = ngx.location.capture(\"/call\");\n                if res.status == 200 then\n                    ngx.print(\"ok\")\n                else\n                    ngx.say(\"error\")\n                end\n            else\n            -- luajit will get a segmentation fault with bad bytecode,\n            -- so here just skip this case for luajit\n                ngx.say(\"error\")\n            end\n        ';\n    }\n    location /call {\n        content_by_lua_file $realpath_root/test.lua;\n    }\n--- request eval\n\"POST /save_call\n\\x1b\\x4c\\x75\\x61\\x51\\x00\\x01\\x04\\x08\\x04\\x08\\x00\\x0a\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x74\\x65\\x73\\x74\\x2e\\x6c\\x75\\x61\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x02\\x05\\x00\\x00\\x00\\xff\\xff\\xff\\xff\\x06\\x40\\x40\\x00\\x41\\x80\\x00\\x00\\x1c\\x40\\x00\\x01\\x1e\\x00\\x80\\x00\\x03\\x00\\x00\\x00\\x04\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x6e\\x67\\x78\\x00\\x04\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x73\\x61\\x79\\x00\\x04\\x06\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x68\\x65\\x6c\\x6c\\x6f\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\n\\x1b\\x4c\\x4a\\x01\\x02\\x29\\x02\\x00\\x02\\x00\\x03\\x00\\x05\\xff\\xff\\xff\\xff\\x37\\x00\\x01\\x00\\x25\\x01\\x02\\x00\\x3e\\x00\\x02\\x01\\x47\\x00\\x01\\x00\\x0a\\x68\\x65\\x6c\\x6c\\x6f\\x08\\x73\\x61\\x79\\x08\\x6e\\x67\\x78\\x00\"\n--- response_body\nerror\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: stripped(lua) & no stripped(luajit)\n--- config\n    root html;\n    location /save_call {\n        content_by_lua '\n            ngx.req.read_body();\n            local b = ngx.req.get_body_data();\n            local f = io.open(ngx.var.realpath_root..\"/test.lua\", \"w\");\n            -- luajit bytecode: sub(149,-1), lua bytecode: sub(1,147)\n            if jit then\n                if not string.find(jit.version, \"LuaJIT 2.0\") then\n                    ngx.say(\"test skipped\")\n                    return\n                end\n\n                f:write(string.sub(b, 119));\n            else\n                f:write(string.sub(b, 1, 117));\n            end\n            f:close(); res = ngx.location.capture(\"/call\");\n            ngx.print(res.body)\n        ';\n    }\n    location /call {\n        content_by_lua_file $realpath_root/test.lua;\n    }\n--- request eval\n\"POST /save_call\n\\x1b\\x4c\\x75\\x61\\x51\\x00\\x01\\x04\\x08\\x04\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x02\\x05\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x40\\x40\\x00\\x41\\x80\\x00\\x00\\x1c\\x40\\x00\\x01\\x1e\\x00\\x80\\x00\\x03\\x00\\x00\\x00\\x04\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x6e\\x67\\x78\\x00\\x04\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x73\\x61\\x79\\x00\\x04\\x06\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x68\\x65\\x6c\\x6c\\x6f\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\n\\x1b\\x4c\\x4a\\x01\\x00\\x09\\x40\\x74\\x65\\x73\\x74\\x2e\\x6c\\x75\\x61\\x32\\x02\\x00\\x02\\x00\\x03\\x00\\x05\\x06\\x00\\x02\\x34\\x00\\x00\\x00\\x37\\x00\\x01\\x00\\x25\\x01\\x02\\x00\\x3e\\x00\\x02\\x01\\x47\\x00\\x01\\x00\\x0a\\x68\\x65\\x6c\\x6c\\x6f\\x08\\x73\\x61\\x79\\x08\\x6e\\x67\\x78\\x01\\x01\\x01\\x01\\x01\\x00\\x00\"\n--- response_body_like chop\n^(?:hello|test skipped)$\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: generate & load bytecode for LuaJIT (stripped)\n--- config\n    location = /t {\n        content_by_lua '\n            local bcsave = require \"jit.bcsave\"\n            if jit then\n                local prefix = \"$TEST_NGINX_SERVER_ROOT\"\n                local infile = prefix .. \"/html/a.lua\"\n                local outfile = prefix .. \"/html/a.luac\"\n                bcsave.start(\"-s\", infile, outfile)\n                return ngx.exec(\"/call\")\n            end\n\n            ngx.say(\"test skipped!\")\n        ';\n    }\n    location = /call {\n        content_by_lua_file html/a.luac;\n    }\n--- request\n    GET /t\n\n--- user_files\n>>> a.lua\nngx.status = 201 ngx.say(\"hello from Lua!\")\n--- response_body_like chop\n^(?:hello from Lua!|test skipped!)$\n--- no_error_log\n[error]\n--- error_code: 201\n\n\n\n=== TEST 8: generate & load bytecode for LuaJIT (not stripped)\n--- config\n    location = /t {\n        content_by_lua '\n            local bcsave = require \"jit.bcsave\"\n            if jit then\n                local prefix = \"$TEST_NGINX_SERVER_ROOT\"\n                local infile = prefix .. \"/html/a.lua\"\n                local outfile = prefix .. \"/html/a.luac\"\n                bcsave.start(\"-g\", infile, outfile)\n                return ngx.exec(\"/call\")\n            end\n\n            ngx.say(\"test skipped!\")\n        ';\n    }\n    location = /call {\n        content_by_lua_file html/a.luac;\n    }\n--- request\n    GET /t\n\n--- user_files\n>>> a.lua\nngx.status = 201 ngx.say(\"hello from Lua!\")\n--- response_body_like chop\n^(?:hello from Lua!|test skipped!)$\n--- no_error_log\n[error]\n--- error_code: 201\n\n\n\n=== TEST 9: bytecode (not stripped)\n--- config\n    location = /t {\n        content_by_lua_block {\n            local f = assert(loadstring(\"local a = 1 ngx.say('a = ', a)\", \"=code\"))\n            local bc = string.dump(f)\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/a.luac\", \"w\"))\n            f:write(bc)\n            f:close()\n        }\n    }\n\n    location = /t2 {\n        content_by_lua_file html/a.luac;\n    }\n\n    location = /main {\n        echo_location /t;\n        echo_location /t2;\n    }\n--- request\nGET /main\n--- response_body\na = 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: bytecode (stripped)\n--- config\n    location = /t {\n        content_by_lua_block {\n            local f = assert(loadstring(\"local a = 1 ngx.say('a = ', a)\", \"=code\"))\n            local bc = string.dump(f, true)\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/a.luac\", \"w\"))\n            f:write(bc)\n            f:close()\n        }\n    }\n\n    location = /t2 {\n        content_by_lua_file html/a.luac;\n    }\n\n    location = /main {\n        echo_location /t;\n        echo_location /t2;\n    }\n--- request\nGET /main\n--- response_body\na = 1\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/082-body-filter-2.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_EVENT_TYPE} && $ENV{TEST_NGINX_EVENT_TYPE} ne 'poll') {\n        $SkipReason = \"unavailable for the event type '$ENV{TEST_NGINX_EVENT_TYPE}'\";\n\n    } elsif ($ENV{TEST_NGINX_USE_HTTP3}) {\n        $SkipReason = \"http3 does not support mockeagain\";\n\n    } elsif ($ENV{TEST_NGINX_USE_HTTP2}) {\n        $SkipReason = \"http2 does not support mockeagain\";\n\n    } else {\n        if ($ENV{LD_PRELOAD} && $ENV{LD_PRELOAD} =~ /\\bmockeagain\\.so\\b/) {\n            $ENV{TEST_NGINX_POSTPONE_OUTPUT} = 1;\n            $ENV{TEST_NGINX_EVENT_TYPE} = 'poll';\n            $ENV{MOCKEAGAIN}='w'\n        } else {\n            $SkipReason = \"'mockeagain.so' does not appear to be preloaded \"\n                . \"with 'LD_PRELOAD'\";\n        }\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nlog_level('debug');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 5);\n\n#no_diff();\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: check ctx->busy_bufs\n--- config\n    location /t {\n        postpone_output 1;\n        content_by_lua_block {\n            for i = 1, 5 do\n                ngx.say(i, \": Hello World!\")\n            end\n        }\n\n        body_filter_by_lua_block {\n            ngx.arg[1] = ngx.arg[1]\n        }\n    }\n--- request\nGET /t\n--- response_body\n1: Hello World!\n2: Hello World!\n3: Hello World!\n4: Hello World!\n5: Hello World!\n\n--- error_log\nwaiting body filter busy buffer to be sent\nlua say response has busy bufs\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: arg[1] not change and say long string\n--- config\n    location /t {\n        header_filter_by_lua_block {ngx.header.content_length = nil}\n        body_filter_by_lua_block {\n            local function anyting_not_change_arg1()\n                return\n            end\n            anyting_not_change_arg1()\n        }\n        content_by_lua_block {\n            for i = 1, 100 do\n                ngx.say(\"12345678901234567890\")\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body eval\n(\"12345678901234567890\\n\" x 100)\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n\n\n=== TEST 3: arg[1] not change and chunked_transfer_encoding off\n--- config\n    location /t {\n        header_filter_by_lua_block {ngx.header.content_length = nil}\n        body_filter_by_lua_block {\n            local function anyting_not_change_arg1()\n                return\n            end\n            anyting_not_change_arg1()\n        }\n        chunked_transfer_encoding off;\n        content_by_lua_block {\n            for i = 1, 100 do\n                ngx.say(\"12345678901234567890123456789012345678901234567890\"..\"_\"..tostring(i/3))\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\n12345678901234567890123456789012345678901234567890_0.33333333333333\n12345678901234567890123456789012345678901234567890_0.66666666666667\n12345678901234567890123456789012345678901234567890_1\n12345678901234567890123456789012345678901234567890_1.3333333333333\n12345678901234567890123456789012345678901234567890_1.6666666666667\n12345678901234567890123456789012345678901234567890_2\n12345678901234567890123456789012345678901234567890_2.3333333333333\n12345678901234567890123456789012345678901234567890_2.6666666666667\n12345678901234567890123456789012345678901234567890_3\n12345678901234567890123456789012345678901234567890_3.3333333333333\n12345678901234567890123456789012345678901234567890_3.6666666666667\n12345678901234567890123456789012345678901234567890_4\n12345678901234567890123456789012345678901234567890_4.3333333333333\n12345678901234567890123456789012345678901234567890_4.6666666666667\n12345678901234567890123456789012345678901234567890_5\n12345678901234567890123456789012345678901234567890_5.3333333333333\n12345678901234567890123456789012345678901234567890_5.6666666666667\n12345678901234567890123456789012345678901234567890_6\n12345678901234567890123456789012345678901234567890_6.3333333333333\n12345678901234567890123456789012345678901234567890_6.6666666666667\n12345678901234567890123456789012345678901234567890_7\n12345678901234567890123456789012345678901234567890_7.3333333333333\n12345678901234567890123456789012345678901234567890_7.6666666666667\n12345678901234567890123456789012345678901234567890_8\n12345678901234567890123456789012345678901234567890_8.3333333333333\n12345678901234567890123456789012345678901234567890_8.6666666666667\n12345678901234567890123456789012345678901234567890_9\n12345678901234567890123456789012345678901234567890_9.3333333333333\n12345678901234567890123456789012345678901234567890_9.6666666666667\n12345678901234567890123456789012345678901234567890_10\n12345678901234567890123456789012345678901234567890_10.333333333333\n12345678901234567890123456789012345678901234567890_10.666666666667\n12345678901234567890123456789012345678901234567890_11\n12345678901234567890123456789012345678901234567890_11.333333333333\n12345678901234567890123456789012345678901234567890_11.666666666667\n12345678901234567890123456789012345678901234567890_12\n12345678901234567890123456789012345678901234567890_12.333333333333\n12345678901234567890123456789012345678901234567890_12.666666666667\n12345678901234567890123456789012345678901234567890_13\n12345678901234567890123456789012345678901234567890_13.333333333333\n12345678901234567890123456789012345678901234567890_13.666666666667\n12345678901234567890123456789012345678901234567890_14\n12345678901234567890123456789012345678901234567890_14.333333333333\n12345678901234567890123456789012345678901234567890_14.666666666667\n12345678901234567890123456789012345678901234567890_15\n12345678901234567890123456789012345678901234567890_15.333333333333\n12345678901234567890123456789012345678901234567890_15.666666666667\n12345678901234567890123456789012345678901234567890_16\n12345678901234567890123456789012345678901234567890_16.333333333333\n12345678901234567890123456789012345678901234567890_16.666666666667\n12345678901234567890123456789012345678901234567890_17\n12345678901234567890123456789012345678901234567890_17.333333333333\n12345678901234567890123456789012345678901234567890_17.666666666667\n12345678901234567890123456789012345678901234567890_18\n12345678901234567890123456789012345678901234567890_18.333333333333\n12345678901234567890123456789012345678901234567890_18.666666666667\n12345678901234567890123456789012345678901234567890_19\n12345678901234567890123456789012345678901234567890_19.333333333333\n12345678901234567890123456789012345678901234567890_19.666666666667\n12345678901234567890123456789012345678901234567890_20\n12345678901234567890123456789012345678901234567890_20.333333333333\n12345678901234567890123456789012345678901234567890_20.666666666667\n12345678901234567890123456789012345678901234567890_21\n12345678901234567890123456789012345678901234567890_21.333333333333\n12345678901234567890123456789012345678901234567890_21.666666666667\n12345678901234567890123456789012345678901234567890_22\n12345678901234567890123456789012345678901234567890_22.333333333333\n12345678901234567890123456789012345678901234567890_22.666666666667\n12345678901234567890123456789012345678901234567890_23\n12345678901234567890123456789012345678901234567890_23.333333333333\n12345678901234567890123456789012345678901234567890_23.666666666667\n12345678901234567890123456789012345678901234567890_24\n12345678901234567890123456789012345678901234567890_24.333333333333\n12345678901234567890123456789012345678901234567890_24.666666666667\n12345678901234567890123456789012345678901234567890_25\n12345678901234567890123456789012345678901234567890_25.333333333333\n12345678901234567890123456789012345678901234567890_25.666666666667\n12345678901234567890123456789012345678901234567890_26\n12345678901234567890123456789012345678901234567890_26.333333333333\n12345678901234567890123456789012345678901234567890_26.666666666667\n12345678901234567890123456789012345678901234567890_27\n12345678901234567890123456789012345678901234567890_27.333333333333\n12345678901234567890123456789012345678901234567890_27.666666666667\n12345678901234567890123456789012345678901234567890_28\n12345678901234567890123456789012345678901234567890_28.333333333333\n12345678901234567890123456789012345678901234567890_28.666666666667\n12345678901234567890123456789012345678901234567890_29\n12345678901234567890123456789012345678901234567890_29.333333333333\n12345678901234567890123456789012345678901234567890_29.666666666667\n12345678901234567890123456789012345678901234567890_30\n12345678901234567890123456789012345678901234567890_30.333333333333\n12345678901234567890123456789012345678901234567890_30.666666666667\n12345678901234567890123456789012345678901234567890_31\n12345678901234567890123456789012345678901234567890_31.333333333333\n12345678901234567890123456789012345678901234567890_31.666666666667\n12345678901234567890123456789012345678901234567890_32\n12345678901234567890123456789012345678901234567890_32.333333333333\n12345678901234567890123456789012345678901234567890_32.666666666667\n12345678901234567890123456789012345678901234567890_33\n12345678901234567890123456789012345678901234567890_33.333333333333\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n\n\n=== TEST 4: set resp body nil with ngx.arg[1] first\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.say(\"Hello World!\")\n        }\n\n        body_filter_by_lua_block {\n            ngx.arg[1] = \"\"\n            ngx.arg[2] = true\n        }\n    }\n--- request\nGET /t\n--- response_body\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n\n\n=== TEST 5: set resp body nil with ngx.arg[2] first\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.say(\"Hello World!\")\n        }\n\n        body_filter_by_lua_block {\n            ngx.arg[2] = true\n            ngx.arg[1] = \"\"\n        }\n    }\n--- request\nGET /t\n--- response_body\n--- no_error_log\n[error]\n[alert]\n[crit]\n"
  },
  {
    "path": "t/082-body-filter.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nlog_level('debug');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 11);\n\n#no_diff();\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: read chunks (inline)\n--- config\n    location /read {\n        echo -n hello world;\n        echo -n hiya globe;\n\n        body_filter_by_lua '\n            local chunk, eof = ngx.arg[1], ngx.arg[2]\n            print(\"chunk: [\", chunk, \"], eof: \", eof)\n        ';\n    }\n--- request\nGET /read\n--- response_body chop\nhello worldhiya globe\n--- error_log\nchunk: [hello world], eof: false\nchunk: [hiya globe], eof: false\nchunk: [], eof: true\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: read chunks (file)\n--- config\n    location /read {\n        echo -n hello world;\n        echo -n hiya globe;\n\n        body_filter_by_lua_file html/a.lua;\n    }\n--- user_files\n>>> a.lua\nlocal chunk, eof = ngx.arg[1], ngx.arg[2]\nprint(\"chunk: [\", chunk, \"], eof: \", eof)\n--- request\nGET /read\n--- response_body chop\nhello worldhiya globe\n--- error_log\nchunk: [hello world], eof: false\nchunk: [hiya globe], eof: false\nchunk: [], eof: true\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: read chunks (user module)\n--- http_config\n    lua_package_path \"$prefix/html/?.lua;;\";\n--- config\n    location /read {\n        echo -n hello world;\n        echo -n hiya globe;\n\n        body_filter_by_lua '\n            local foo = require \"foo\"\n            foo.go()\n        ';\n    }\n--- user_files\n>>> foo.lua\nmodule(\"foo\", package.seeall)\n\nfunction go()\n    -- ngx.say(\"Hello\")\n    local chunk, eof = ngx.arg[1], ngx.arg[2]\n    print(\"chunk: [\", chunk, \"], eof: \", eof)\nend\n--- request\nGET /read\n--- response_body chop\nhello worldhiya globe\n--- error_log\nchunk: [hello world], eof: false\nchunk: [hiya globe], eof: false\nchunk: [], eof: true\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: rewrite chunks (upper all)\n--- config\n    location /t {\n        echo hello world;\n        echo hiya globe;\n\n        body_filter_by_lua '\n            ngx.arg[1] = string.upper(ngx.arg[1])\n        ';\n    }\n--- request\nGET /t\n--- response_body\nHELLO WORLD\nHIYA GLOBE\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: rewrite chunks (truncate data)\n--- config\n    location /t {\n        echo hello world;\n        echo hiya globe;\n\n        body_filter_by_lua '\n            local chunk = ngx.arg[1]\n            if string.match(chunk, \"hello\") then\n                ngx.arg[1] = string.upper(chunk)\n                ngx.arg[2] = true\n                return\n            end\n\n            ngx.arg[1] = nil\n        ';\n    }\n--- request\nGET /t\n--- response_body\nHELLO WORLD\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: set eof back and forth\n--- config\n    location /t {\n        echo hello world;\n        echo hiya globe;\n\n        body_filter_by_lua '\n            local chunk = ngx.arg[1]\n            if string.match(chunk, \"hello\") then\n                ngx.arg[1] = string.upper(chunk)\n                ngx.arg[2] = true\n                ngx.arg[2] = false\n                ngx.arg[2] = true\n                return\n            end\n\n            ngx.arg[1] = nil\n            ngx.arg[2] = true\n            ngx.arg[2] = false\n        ';\n    }\n--- request\nGET /t\n--- response_body\nHELLO WORLD\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: set eof to original\n--- config\n    location /t {\n        echo hello world;\n        echo hiya globe;\n\n        body_filter_by_lua '\n            local chunk, eof = ngx.arg[1], ngx.arg[2]\n            ngx.arg[2] = eof\n        ';\n    }\n--- request\nGET /t\n--- response_body\nhello world\nhiya globe\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: set eof to original\n--- config\n    location /t {\n        echo hello world;\n        echo hiya globe;\n\n        body_filter_by_lua '\n            local chunk, eof = ngx.arg[1], ngx.arg[2]\n            ngx.arg[2] = eof\n        ';\n    }\n--- request\nGET /t\n--- response_body\nhello world\nhiya globe\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: fully buffered output (string scalar)\n--- config\n    location /t {\n        echo hello world;\n        echo hiya globe;\n\n        body_filter_by_lua '\n            local chunk, eof = ngx.arg[1], ngx.arg[2]\n            local buf = ngx.ctx.buf\n\n            if eof then\n                if buf then\n                    ngx.arg[1] = \"[\" .. buf .. chunk .. \"]\"\n                    return\n                end\n\n                return\n            end\n\n            if buf then\n                ngx.ctx.buf = buf .. chunk\n            else\n                ngx.ctx.buf = chunk\n            end\n\n            ngx.arg[1] = nil\n        ';\n    }\n--- request\nGET /t\n--- response_body chop\n[hello world\nhiya globe\n]\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: fully buffered output (string table)\n--- config\n    location /t {\n        echo hello world;\n        echo hiya globe;\n\n        body_filter_by_lua '\n            local chunk, eof = ngx.arg[1], ngx.arg[2]\n            local buf = ngx.ctx.buf\n\n            if eof then\n                if buf then\n                    ngx.arg[1] = {\"[\", buf, chunk, \"]\"}\n                    return\n                end\n\n                return\n            end\n\n            if buf then\n                ngx.ctx.buf = {buf, chunk}\n            else\n                ngx.ctx.buf = chunk\n            end\n\n            ngx.arg[1] = nil\n        ';\n    }\n--- request\nGET /t\n--- response_body chop\n[hello world\nhiya globe\n]\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: abort via user error (string)\n--- config\n    location /t {\n        echo hello world;\n        echo_flush;\n        echo hiya globe;\n\n        body_filter_by_lua '\n            local chunk, eof = ngx.arg[1], ngx.arg[2]\n            if eof then\n                error(\"something bad happened!\")\n            end\n        ';\n    }\n--- request\nGET /t\n--- ignore_response\n--- error_log\nfailed to run body_filter_by_lua*: body_filter_by_lua(nginx.conf:49):4: something bad happened!\n\n\n\n=== TEST 12: abort via user error (nil)\n--- config\n    location /t {\n        echo hello world;\n        echo_flush;\n        echo hiya globe;\n\n        body_filter_by_lua '\n            local chunk, eof = ngx.arg[1], ngx.arg[2]\n            if eof then\n                error(nil)\n            end\n        ';\n    }\n--- request\nGET /t\n--- ignore_response\n--- error_log\nfailed to run body_filter_by_lua*: unknown reason\n\n\n\n=== TEST 13: abort via return NGX_ERROR\n--- config\n    location /t {\n        echo hello world;\n        echo_flush;\n        echo hiya globe;\n\n        body_filter_by_lua '\n            local chunk, eof = ngx.arg[1], ngx.arg[2]\n            if eof then\n                return ngx.ERROR\n            end\n        ';\n    }\n--- request\nGET /t\n--- ignore_response\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: using body_filter_by_lua and header_filter_by_lua at the same time\n--- config\n    location /t {\n        content_by_lua '\n            ngx.header.content_length = 12\n            ngx.say(\"Hello World\")\n        ';\n\n        header_filter_by_lua 'ngx.header.content_length = nil';\n\n        body_filter_by_lua '\n            ngx.arg[1] = ngx.arg[1] .. \"aaa\"\n        ';\n    }\n--- request\nGET /t\n--- response_body chop\nHello World\naaaaaa\n--- response_headers\n!content-length\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: table arguments to ngx.arg[1] (github issue #54)\n--- config\n    location /t {\n        echo -n hello;\n\n        body_filter_by_lua '\n            if ngx.arg[1] ~= \"\" then\n                ngx.arg[1] = {{ngx.arg[1]}, \"!\", \"\\\\n\"}\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nhello!\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: fully buffered output (string scalar, buffering to disk by ngx_proxy)\n--- config\n    location /t {\n        proxy_pass http://127.0.0.1:$server_port/stub;\n        proxy_buffers 2 256;\n        proxy_busy_buffers_size 256;\n        proxy_buffer_size 256;\n\n        body_filter_by_lua '\n            local chunk, eof = ngx.arg[1], ngx.arg[2]\n            local buf = ngx.ctx.buf\n\n            if eof then\n                if buf then\n                    ngx.arg[1] = \"[\" .. buf .. chunk .. \"]\"\n                    return\n                end\n\n                return\n            end\n\n            if buf then\n                ngx.ctx.buf = buf .. chunk\n            else\n                ngx.ctx.buf = chunk\n            end\n\n            ngx.arg[1] = nil\n        ';\n    }\n\n    location = /stub {\n        echo_duplicate 512 \"a\";\n        echo_duplicate 512 \"b\";\n    }\n--- request\nGET /t\n--- response_body eval\n\"[\" . (\"a\" x 512) . (\"b\" x 512) . \"]\";\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 17: backtrace\n--- config\n    location /t {\n        body_filter_by_lua '\n            local bar\n            local function foo()\n                bar()\n            end\n\n            function bar()\n                error(\"something bad happened\")\n            end\n\n            foo()\n        ';\n        echo ok;\n    }\n--- request\n    GET /t\n--- ignore_response\n--- error_log\nsomething bad happened\nstack traceback:\nin function 'error'\nin function 'bar'\nin function 'foo'\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP\\/3 stream 0 reset by server#\n\n\n\n=== TEST 18: setting \"eof\" in subrequests\n--- config\n    location /t {\n        echo_location /read;\n        echo_location /read;\n    }\n\n    location /read {\n        echo -n hello world;\n        echo -n hiya globe;\n\n        body_filter_by_lua '\n            ngx.arg[2] = 1\n        ';\n    }\n--- request\nGET /t\n--- response_body chop\nhello worldhello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: Lua file does not exist\n--- config\n    location /lua {\n        echo ok;\n        body_filter_by_lua_file html/test2.lua;\n    }\n--- user_files\n>>> test.lua\nv = ngx.var[\"request_uri\"]\nngx.print(\"request_uri: \", v, \"\\n\")\n--- request\nGET /lua?a=1&b=2\n--- ignore_response\n--- error_log eval\nqr/failed to load external Lua file \".*?test2\\.lua\": cannot open .*? No such file or directory/\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP\\/3 stream 0 reset by server#\n\n\n\n=== TEST 20: overwrite eof\n--- config\n    location /read {\n        return 200 \"hello world\";\n\n        body_filter_by_lua '\n            local chunk, eof = ngx.arg[1], ngx.arg[2]\n            if eof then\n                ngx.arg[2] = false\n            end\n        ';\n    }\n\n    location = /t {\n        content_by_lua '\n            local res = ngx.location.capture(\"/read\")\n            ngx.say(\"truncated: \", res.truncated)\n        ';\n    }\n--- request\nGET /t\n--- response_body\ntruncated: true\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 21: zero-size bufs\n--- config\n    location = /t {\n        echo hello;\n        echo world;\n\n        body_filter_by_lua '\n            ngx.arg[1] = \"\"\n        ';\n    }\n--- request\nGET /t\n--- response_body\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 22: body filter + ngx.say() (github issue #386)\n--- config\n    postpone_output 1;\n    location = /t {\n        header_filter_by_lua 'ngx.header.content_length = nil';\n\n        body_filter_by_lua '\n            -- do return end\n            if not ngx.ctx.chunks then\n                ngx.ctx.chunks = {}\n            end\n\n            table.insert(ngx.ctx.chunks, ngx.arg[1])\n            print(\"got chunk \", ngx.arg[1])\n            ngx.arg[1] = nil\n\n            if ngx.arg[2] then\n                print(\"seen eof: \", string.upper(table.concat(ngx.ctx.chunks)))\n                ngx.arg[1] = string.upper(table.concat(ngx.ctx.chunks))\n            end\n        ';\n\n        content_by_lua '\n            for i = 1, 10 do\n                assert(ngx.say(\"hello world\"))\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\n\"HELLO WORLD\\n\" x 10\n\n--- stap2\nglobal active = 1\nF(ngx_http_lua_body_filter_by_chunk) {\n    printf(\"body filter by lua: %p: %s\\n\", $in, ngx_chain_dump($in))\n}\n\nF(ngx_http_write_filter) {\n    printf(\"write filter: %p: %s\\n\", $in, ngx_chain_dump($in))\n}\n\n\nF(ngx_output_chain) {\n    #printf(\"ctx->in: %s\\n\", ngx_chain_dump($ctx->in))\n    #printf(\"ctx->busy: %s\\n\", ngx_chain_dump($ctx->busy))\n    printf(\"output chain %p: %s\\n\", $in, ngx_chain_dump($in))\n}\nF(ngx_linux_sendfile_chain) {\n    printf(\"linux sendfile chain: %s\\n\", ngx_chain_dump($in))\n}\nF(ngx_chain_writer) {\n    printf(\"chain writer ctx out: %p\\n\", $data)\n    printf(\"nginx chain writer: %s\\n\", ngx_chain_dump($in))\n}\nprobe syscall.writev {\n    if (active && pid() == target()) {\n        printf(\"writev(%s)\", ngx_iovec_dump($vec, $vlen))\n        /*\n        for (i = 0; i < $vlen; i++) {\n            printf(\" %p [%s]\", $vec[i]->iov_base, text_str(user_string_n($vec[i]->iov_base, $vec[i]->iov_len)))\n        }\n        */\n    }\n}\nprobe syscall.writev.return {\n    if (active && pid() == target()) {\n        printf(\" = %s\\n\", retstr)\n    }\n}\n\n--- stap_out2\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 23: body filter + ngx.say() (github issue #386), with flush\n--- config\n    location = /t {\n        header_filter_by_lua 'ngx.header.content_length = nil';\n\n        body_filter_by_lua '\n            -- do return end\n            if not ngx.ctx.chunks then\n                ngx.ctx.chunks = {}\n            end\n\n            table.insert(ngx.ctx.chunks, ngx.arg[1])\n            print(\"got chunk \", ngx.arg[1])\n            ngx.arg[1] = nil\n\n            if ngx.arg[2] then\n                print(\"seen eof: \", string.upper(table.concat(ngx.ctx.chunks)))\n                ngx.arg[1] = string.upper(table.concat(ngx.ctx.chunks))\n            end\n        ';\n\n        content_by_lua '\n            for i = 1, 10 do\n                assert(ngx.say(\"hello world\"))\n                ngx.flush(true)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\n\"HELLO WORLD\\n\" x 10\n\n--- stap\nF(ngx_http_write_filter) {\n    for (cl = $in; cl; cl = @cast(cl, \"ngx_chain_t\")->next) {\n        if (@cast(cl, \"ngx_chain_t\")->buf->flush) {\n            printf(\"seen flush buf.\\n\")\n        }\n\n        if (@cast(cl, \"ngx_chain_t\")->buf->last_buf) {\n            printf(\"seen last buf.\\n\")\n        }\n    }\n}\n\n--- stap_out_like eval\nqr/^(?:seen flush buf\\.\n){10,}seen last buf\\.\n$/\n\n--- stap2\nglobal active = 1\nF(ngx_http_lua_body_filter_by_chunk) {\n    printf(\"body filter by lua: %p: %s\\n\", $in, ngx_chain_dump($in))\n}\n\nF(ngx_http_write_filter) {\n    printf(\"write filter: %p: %s\\n\", $in, ngx_chain_dump($in))\n}\n\nF(ngx_http_charset_body_filter) {\n    printf(\"charset body filter: %p: %s\\n\", $in, ngx_chain_dump($in))\n}\n\nF(ngx_output_chain) {\n    #printf(\"ctx->in: %s\\n\", ngx_chain_dump($ctx->in))\n    #printf(\"ctx->busy: %s\\n\", ngx_chain_dump($ctx->busy))\n    printf(\"output chain %p: %s\\n\", $in, ngx_chain_dump($in))\n}\n\nF(ngx_linux_sendfile_chain) {\n    printf(\"linux sendfile chain: %s\\n\", ngx_chain_dump($in))\n}\n\nF(ngx_chain_writer) {\n    printf(\"chain writer ctx out: %p\\n\", $data)\n    printf(\"nginx chain writer: %s\\n\", ngx_chain_dump($in))\n}\n\nprobe syscall.writev {\n    if (active && pid() == target()) {\n        printf(\"writev(%s)\", ngx_iovec_dump($vec, $vlen))\n        /*\n        for (i = 0; i < $vlen; i++) {\n            printf(\" %p [%s]\", $vec[i]->iov_base, text_str(user_string_n($vec[i]->iov_base, $vec[i]->iov_len)))\n        }\n        */\n    }\n}\n\nprobe syscall.writev.return {\n    if (active && pid() == target()) {\n        printf(\" = %s\\n\", retstr)\n    }\n}\n\n--- stap_out2\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 24: clear ngx.arg[1] and then read it\n--- config\n    location /t {\n        echo hello;\n        echo world;\n\n        body_filter_by_lua '\n            ngx.arg[1] = nil\n            local data = ngx.arg[1]\n            print([[data chunk: \"]], data, [[\"]])\n\n            ngx.arg[1] = \"\"\n            data = ngx.arg[1]\n            print([[data chunk 2: \"]], data, [[\"]])\n        ';\n    }\n--- request\nGET /t\n--- response_body\n--- log_level: info\n--- grep_error_log eval: qr/data chunk(?: \\d+)?: [^,]+/\n--- grep_error_log_out\ndata chunk: \"\"\ndata chunk 2: \"\"\ndata chunk: \"\"\ndata chunk 2: \"\"\ndata chunk: \"\"\ndata chunk 2: \"\"\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 25: clear ngx.arg[1] and then read ngx.arg[2]\n--- config\n    location /t {\n        echo hello;\n        echo world;\n\n        body_filter_by_lua '\n            ngx.arg[1] = nil\n            local eof = ngx.arg[2]\n            print([[eof: ]], eof)\n\n            ngx.arg[1] = \"\"\n            eof = ngx.arg[2]\n            print([[eof 2: ]], eof)\n        ';\n    }\n--- request\nGET /t\n--- response_body\n--- log_level: info\n--- grep_error_log eval: qr/eof(?: \\d+)?: [^,]+/\n--- grep_error_log_out\neof: false\neof 2: false\neof: false\neof 2: false\neof: true\neof 2: true\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 26: no ngx.print\n--- config\n    location /lua {\n        echo ok;\n        body_filter_by_lua \"ngx.print(32) return 1\";\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of body_filter_by_lua*\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP\\/3 stream 0 reset by server#\n\n\n\n=== TEST 27: syntax error in body_filter_by_lua_block\n--- config\n    location /lua {\n\n        body_filter_by_lua_block {\n            'for end';\n        }\n        content_by_lua_block {\n            ngx.say(\"Hello world\")\n        }\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nfailed to load inlined Lua code: body_filter_by_lua(nginx.conf:41):2: unexpected symbol near ''for end''\n--- no_error_log\nno_such_error1\nno_such_error2\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP\\/3 stream 0 reset by server#\n\n\n\n=== TEST 28: syntax error in second body_by_lua_block\n--- config\n    location /foo {\n        body_filter_by_lua_block {\n            'for end';\n        }\n        content_by_lua_block {\n            ngx.say(\"Hello world\")\n        }\n    }\n\n    location /lua {\n        body_filter_by_lua_block {\n            'for end';\n        }\n        content_by_lua_block {\n            ngx.say(\"Hello world\")\n        }\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nfailed to load inlined Lua code: body_filter_by_lua(nginx.conf:49):2: unexpected symbol near ''for end''\n--- no_error_log\nno_such_error1\nno_such_error2\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP\\/3 stream 0 reset by server#\n"
  },
  {
    "path": "t/083-bad-sock-self.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\nour $HtmlDir = html_dir;\n\n#$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\nno_long_string();\n#no_diff();\n#log_level 'warn';\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: receive\n--- config\n    location /t {\n        content_by_lua '\n            local sock, err = ngx.req.socket()\n            sock.receive(\"l\")\n        ';\n    }\n--- request\n    POST /t\n--- more_headers: Content-Length: 1024\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad argument #1 to 'receive' (table expected, got string)\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 2: receiveuntil\n--- config\n    location /t {\n        content_by_lua '\n            local sock, err = ngx.req.socket()\n            sock.receiveuntil(32, \"ab\")\n        ';\n    }\n--- request\n    POST /t\n--- more_headers: Content-Length: 1024\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad argument #1 to 'receiveuntil' (table expected, got number)\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 3: send (bad arg number)\n--- config\n    location /t {\n        content_by_lua '\n            local sock, err = ngx.socket.tcp()\n            sock.send(\"hello\")\n        ';\n    }\n--- request\n    GET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nexpecting 2 arguments (including the object), but got 1\n\n\n\n=== TEST 4: send (bad self)\n--- config\n    location /t {\n        content_by_lua '\n            local sock, err = ngx.socket.tcp()\n            sock.send(\"hello\", 32)\n        ';\n    }\n--- request\n    GET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad argument #1 to 'send' (table expected, got string)\n\n\n\n=== TEST 5: getreusedtimes (bad self)\n--- config\n    location /t {\n        content_by_lua '\n            local sock, err = ngx.socket.tcp()\n            sock.getreusedtimes(2)\n        ';\n    }\n--- request\n    GET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad argument #1 to 'getreusedtimes' (table expected, got number)\n\n\n\n=== TEST 6: close (bad self)\n--- config\n    location /t {\n        content_by_lua '\n            local sock, err = ngx.socket.tcp()\n            sock.close(2)\n        ';\n    }\n--- request\n    GET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad argument #1 to 'close' (table expected, got number)\n\n\n\n=== TEST 7: setkeepalive (bad self)\n--- config\n    location /t {\n        content_by_lua '\n            local sock, err = ngx.socket.tcp()\n            sock.setkeepalive(2)\n        ';\n    }\n--- request\n    GET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nbad argument #1 to 'setkeepalive' (table expected, got number)\n"
  },
  {
    "path": "t/084-inclusive-receiveuntil.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\nno_long_string();\n#no_diff();\n#log_level 'warn';\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: ambiguous boundary patterns (abcabd) - inclusive mode\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"abcabd\", { inclusive = true })\n\n            for i = 1, 3 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo abcabcabdabcabd;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: abcabcabd\nread: abcabd\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: ambiguous boundary patterns (abcabdabcabe 4) - inclusive mode\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"abcabdabcabe\", { inclusive = true })\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo ababcabdabcabe;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: ababcabdabcabe\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: ambiguous boundary patterns (abcabd) - inclusive mode - small buffers\n--- no_http2\n--- config\n    server_tokens off;\n    lua_socket_buffer_size 1;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"abcabd\", { inclusive = true })\n\n            for i = 1, 3 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo abcabcabdabcabd;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: abcabcabd\nread: abcabd\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: inclusive option value nil\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"aa\", { inclusive = nil })\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo abcabcaad;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: abcabc\nfailed to read a line: closed [d\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: inclusive option value false\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"aa\", { inclusive = false })\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo abcabcaad;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: abcabc\nfailed to read a line: closed [d\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: inclusive option value true (aa)\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"aa\", { inclusive = true })\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo abcabcaad;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: abcabcaa\nfailed to read a line: closed [d\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: bad inclusive option value type\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"aa\", { inclusive = \"true\" })\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo abcabcaad;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- ignore_response\n--- error_log\nbad \"inclusive\" option value type: string\n--- no_error_log\n[alert]\n[warn]\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP\\/3 stream 0 reset by server#\n\n\n\n=== TEST 8: bad option table\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"aa\", { inclusive = \"true\" })\n\n            for i = 1, 2 do\n                local line, err, part = reader()\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo abcabcaad;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- ignore_response\n--- error_log\nbad \"inclusive\" option value type: string\n--- no_error_log\n[alert]\n[warn]\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP\\/3 stream 0 reset by server#\n\n\n\n=== TEST 9: ambiguous boundary patterns (--abc), small buffer\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        lua_socket_buffer_size 1;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"--abc\", { inclusive = true })\n\n            for i = 1, 6 do\n                local line, err, part = reader(4)\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a line: \", err, \" [\", part, \"]\")\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo \"hello, world ----abc\";\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: hell\nread: o, w\nread: orld\nread:  ----abc\nfailed to read a line: nil [nil]\nfailed to read a line: closed [\n]\nclose: 1 nil\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: ambiguous boundary patterns (--abc), small buffer, mixed by other reading calls\n--- no_http2\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        lua_socket_buffer_size 1;\n\n        content_by_lua '\n            -- collectgarbage(\"collect\")\n\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", bytes)\n\n            local read_headers = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local headers, err, part = read_headers()\n            if not headers then\n                ngx.say(\"failed to read headers: \", err, \" [\", part, \"]\")\n            end\n\n            local reader = sock:receiveuntil(\"--abc\", { inclusive = true })\n\n            for i = 1, 7 do\n                local line, err, part = reader(4)\n                if line then\n                    ngx.say(\"read: \", line)\n\n                else\n                    ngx.say(\"failed to read a chunk: \", err, \" [\", part, \"]\")\n                end\n\n                local data, err, part = sock:receive(1)\n                if not data then\n                    ngx.say(\"failed to read a byte: \", err, \" [\", part, \"]\")\n                    break\n                else\n                    ngx.say(\"read one byte: \", data)\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo \"hello, world ----abc\";\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\nqq{connected: 1\nrequest sent: 57\nread: hell\nread one byte: o\nread: , wo\nread one byte: r\nread: ld -\nread one byte: -\nread: --abc\nread one byte: \n\nfailed to read a chunk: nil [nil]\nfailed to read a byte: closed []\nclose: 1 nil\n}\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/085-if.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3 + 2);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: set_by_lua (if fails)\n--- config\n    location /t {\n        set $true $arg_a;\n        if ($true) {\n            set_by_lua $true 'return tonumber(ngx.var[\"true\"]) + 1';\n            break;\n        }\n        set $true \"empty\";\n\n        echo \"[$true]\";\n    }\n--- request\nGET /t\n--- response_body\n[empty]\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: set_by_lua (if true)\n--- config\n    location /t {\n        set $true $arg_a;\n        if ($true) {\n            set_by_lua $true 'return tonumber(ngx.var[\"true\"]) + 1';\n            break;\n        }\n        set $true \"blah\";\n\n        echo \"[$true]\";\n    }\n--- request\nGET /t?a=2\n--- response_body\n[3]\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: content_by_lua inherited by location if\n--- config\n    location /t {\n        set $true 1;\n        if ($true) {\n            # nothing\n        }\n\n        content_by_lua 'ngx.say(\"hello world\")';\n    }\n--- request\nGET /t\n--- response_body\nhello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: rewrite_by_lua inherited by location if\n--- config\n    location /t {\n        set $true 1;\n        if ($true) {\n            # nothing\n        }\n\n        rewrite_by_lua 'ngx.say(\"hello world\") ngx.exit(200)';\n    }\n--- request\nGET /t\n--- response_body\nhello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: access_by_lua inherited by location if\n--- config\n    location /t {\n        set $true 1;\n        if ($true) {\n            # nothing\n        }\n\n        access_by_lua 'ngx.say(\"hello world\") ngx.exit(200)';\n    }\n--- request\nGET /t\n--- response_body\nhello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: log_by_lua inherited by location if\n--- config\n    location /t {\n        set $true 1;\n        if ($true) {\n            # nothing\n        }\n\n        log_by_lua 'ngx.log(ngx.WARN, \"from log by lua\")';\n        echo hello world;\n    }\n--- request\nGET /t\n--- response_body\nhello world\n--- no_error_log\n[error]\n--- error_log\nfrom log by lua\n\n\n\n=== TEST 7: header_filter_by_lua inherited by location if\n--- config\n    location /t {\n        set $true 1;\n        if ($true) {\n            # nothing\n        }\n\n        header_filter_by_lua 'ngx.header.Foo = \"bah\"';\n        echo hello world;\n    }\n--- request\nGET /t\n--- response_body\nhello world\n--- response_headers\nFoo: bah\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: body_filter_by_lua inherited by location if\n--- config\n    location /t {\n        set $true 1;\n        if ($true) {\n            # nothing\n        }\n\n        body_filter_by_lua 'ngx.arg[1] = string.upper(ngx.arg[1])';\n        echo hello world;\n    }\n--- request\nGET /t\n--- response_body\nHELLO WORLD\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: if is evil for ngx_proxy\nThis test case requires the following patch for the nginx core:\nhttp://mailman.nginx.org/pipermail/nginx-devel/2012-June/002374.html\n--- config\n    location /proxy-pass-uri {\n        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/;\n\n        set $true 1;\n\n        if ($true) {\n            # nothing\n        }\n    }\n--- request\nGET /proxy-pass-uri\n--- response_body_like: It works!\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/086-init-by.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 2);\n\n#no_diff();\n#no_long_string();\nno_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity (inline)\n--- http_config\n    init_by_lua 'foo = \"hello, FOO\"';\n--- config\n    location /lua {\n        content_by_lua 'ngx.say(foo)';\n    }\n--- request\nGET /lua\n--- response_body\nhello, FOO\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: sanity (file)\n--- http_config\n    init_by_lua_file html/init.lua;\n--- config\n    location /lua {\n        content_by_lua 'ngx.say(foo)';\n    }\n--- user_files\n>>> init.lua\nfoo = \"hello, FOO\"\n--- request\nGET /lua\n--- response_body\nhello, FOO\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: require\n--- http_config\n    lua_package_path \"$prefix/html/?.lua;;\";\n    init_by_lua 'require \"blah\"';\n--- config\n    location /lua {\n        content_by_lua '\n            blah.go()\n        ';\n    }\n--- user_files\n>>> blah.lua\nmodule(..., package.seeall)\n\nfunction go()\n    ngx.say(\"hello, blah\")\nend\n--- request\nGET /lua\n--- response_body\nhello, blah\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: shdict (single)\n--- http_config\n    lua_shared_dict dogs 1m;\n    init_by_lua '\n        local dogs = ngx.shared.dogs\n        dogs:set(\"Jim\", 6)\n        dogs:get(\"Jim\")\n    ';\n--- config\n    location /lua {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            ngx.say(\"Jim: \", dogs:get(\"Jim\"))\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nJim: 6\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: shdict (multi)\n--- http_config\n    lua_shared_dict dogs 1m;\n    lua_shared_dict cats 1m;\n    init_by_lua '\n        local dogs = ngx.shared.dogs\n        dogs:set(\"Jim\", 6)\n        dogs:get(\"Jim\")\n        local cats = ngx.shared.cats\n        cats:set(\"Tom\", 2)\n        dogs:get(\"Tom\")\n    ';\n--- config\n    location /lua {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n            ngx.say(\"Jim: \", dogs:get(\"Jim\"))\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nJim: 6\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: print\n--- http_config\n    lua_shared_dict dogs 1m;\n    lua_shared_dict cats 1m;\n    init_by_lua '\n        print(\"log from init_by_lua\")\n    ';\n--- config\n    location /lua {\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- grep_error_log chop\nlog from init_by_lua\n--- grep_error_log_out eval\n[\"log from init_by_lua\\n\", \"\"]\n\n\n\n=== TEST 7: ngx.log\n--- http_config\n    lua_shared_dict dogs 1m;\n    lua_shared_dict cats 1m;\n    init_by_lua '\n        ngx.log(ngx.NOTICE, \"log from init_by_lua\")\n    ';\n--- config\n    location /lua {\n        echo ok;\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- grep_error_log chop\nlog from init_by_lua\n--- grep_error_log_out eval\n[\"log from init_by_lua\\n\", \"\"]\n\n\n\n=== TEST 8: require (with shm defined)\n--- http_config\n    lua_package_path \"$prefix/html/?.lua;;\";\n    lua_shared_dict dogs 1m;\n    init_by_lua 'require \"blah\"';\n--- config\n    location /lua {\n        content_by_lua '\n            blah.go()\n        ';\n    }\n--- user_files\n>>> blah.lua\nmodule(..., package.seeall)\n\nfunction go()\n    ngx.say(\"hello, blah\")\nend\n--- request\nGET /lua\n--- response_body\nhello, blah\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: coroutine API (inlined init_by_lua)\n--- http_config\n    init_by_lua '\n        local function f()\n            foo = 32\n            coroutine.yield(78)\n            bar = coroutine.status(coroutine.running())\n        end\n        local co = coroutine.create(f)\n        local ok, err = coroutine.resume(co)\n        if not ok then\n            print(\"Failed to resume our co: \", err)\n            return\n        end\n        baz = err\n        coroutine.resume(co)\n    ';\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(\"foo = \", foo)\n            ngx.say(\"bar = \", bar)\n            ngx.say(\"baz = \", baz)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nfoo = 32\nbar = running\nbaz = 78\n--- no_error_log\n[error]\nFailed to resume our co: \n\n\n\n=== TEST 10: coroutine API (init_by_lua_file)\n--- http_config\n    init_by_lua_file html/init.lua;\n\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(\"foo = \", foo)\n            ngx.say(\"bar = \", bar)\n            ngx.say(\"baz = \", baz)\n        ';\n    }\n--- request\nGET /lua\n--- user_files\n>>> init.lua\nlocal function f()\n    foo = 32\n    coroutine.yield(78)\n    bar = coroutine.status(coroutine.running())\nend\nlocal co = coroutine.create(f)\nlocal ok, err = coroutine.resume(co)\nif not ok then\n    print(\"Failed to resume our co: \", err)\n    return\nend\nbaz = err\ncoroutine.resume(co)\n\n--- response_body\nfoo = 32\nbar = running\nbaz = 78\n--- no_error_log\n[error]\nFailed to resume our co: \n\n\n\n=== TEST 11: access a field in the ngx. table\n--- http_config\n    init_by_lua '\n        print(\"INIT 1: foo = \", ngx.foo)\n        ngx.foo = 3\n        print(\"INIT 2: foo = \", ngx.foo)\n    ';\n--- config\n    location /t {\n        echo ok;\n    }\n--- request\nGET /t\n--- response_body\nok\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/INIT \\d+: foo = \\S+/\n--- grep_error_log_out eval\n[\n\"INIT 1: foo = nil\nINIT 2: foo = 3\n\",\n\"\",\n]\n\n\n\n=== TEST 12: error in init\n--- http_config\n    init_by_lua_block {\n        error(\"failed to init\")\n    }\n--- config\n    location /t {\n        echo ok;\n    }\n--- must_die\n--- error_log\nfailed to init\n--- error_log\n[error]\n\n\n\n=== TEST 13: syntax error in init_by_lua_block\n--- http_config\n    init_by_lua_block {\n        ngx.log(ngx.debug, \"pass\")\n        error(\"failed to init\"\n        ngx.log(ngx.debug, \"unreachable\")\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"hello world\")\n        }\n    }\n--- must_die\n--- error_log\ninit_by_lua error: init_by_lua(nginx.conf:25):4: ')' expected (to close '(' at line 3) near 'ngx'\n--- no_error_log\nno_such_error_log\n\n\n\n=== TEST 14: syntax error in init_by_lua_file\n--- http_config\n    init_by_lua_file html/init.lua;\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"hello world\")\n        }\n    }\n--- user_files\n>>> init.lua\n    ngx.log(ngx.debug, \"pass\")\n    error(\"failed to init\"\n    ngx.log(ngx.debug, \"unreachable\")\n\n--- must_die\n--- error_log eval\nqr|init_by_lua_file error: .*?/t/servroot\\w*?/html/init.lua:3: '\\)' expected \\(to close '\\(' at line 2\\) near 'ngx'|\n--- no_error_log\nno_such_error_log\n"
  },
  {
    "path": "t/087-udp-socket.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (3 * blocks() + 16);\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\nlog_level 'warn';\n\nno_long_string();\n#no_diff();\n#no_shuffle();\ncheck_accum_error_log();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        #set $port 1234;\n\n        content_by_lua '\n            local socket = ngx.socket\n            -- local socket = require \"socket\"\n\n            local udp = socket.udp()\n\n            local port = ngx.var.port\n            udp:settimeout(1000) -- 1 sec\n\n            local ok, err = udp:setpeername(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected\")\n\n            local req = \"\\\\0\\\\1\\\\0\\\\0\\\\0\\\\1\\\\0\\\\0flush_all\\\\r\\\\n\"\n            local ok, err = udp:send(req)\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local data, err = udp:receive()\n            if not data then\n                ngx.say(\"failed to receive data: \", err)\n                return\n            end\n            ngx.print(\"received \", #data, \" bytes: \", data)\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\n\"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}\"\n--- no_error_log\n[error]\n--- log_level: debug\n--- error_log\nlua udp socket receive buffer size: 65536\n\n\n\n=== TEST 2: multiple parallel queries\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        #set $port 1234;\n\n        content_by_lua '\n            local socket = ngx.socket\n            -- local socket = require \"socket\"\n\n            local udp = socket.udp()\n\n            local port = ngx.var.port\n            udp:settimeout(1000) -- 1 sec\n\n            local ok, err = udp:setpeername(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected\")\n\n            local req = \"\\\\0\\\\1\\\\0\\\\0\\\\0\\\\1\\\\0\\\\0flush_all\\\\r\\\\n\"\n            local ok, err = udp:send(req)\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            req = \"\\\\0\\\\2\\\\0\\\\0\\\\0\\\\1\\\\0\\\\0flush_all\\\\r\\\\n\"\n            ok, err = udp:send(req)\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            ngx.sleep(0.05)\n\n            local data, err = udp:receive()\n            if not data then\n                ngx.say(\"failed to receive data: \", err)\n                return\n            end\n            ngx.print(\"1: received \", #data, \" bytes: \", data)\n\n            data, err = udp:receive()\n            if not data then\n                ngx.say(\"failed to receive data: \", err)\n                return\n            end\n            ngx.print(\"2: received \", #data, \" bytes: \", data)\n        ';\n    }\n--- request\nGET /t\n--- response_body_like eval\n\"^connected\\n\"\n.\"1: received 12 bytes: \"\n.\"\\x{00}[\\1\\2]\\x{00}\\x{00}\\x{00}\\x{01}\\x{00}\\x{00}OK\\x{0d}\\x{0a}\"\n.\"2: received 12 bytes: \"\n.\"\\x{00}[\\1\\2]\\x{00}\\x{00}\\x{00}\\x{01}\\x{00}\\x{00}OK\\x{0d}\\x{0a}\\$\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: access a TCP interface\ntest-nginx use the same port for tcp(http) and udp(http3)\nso need to change to a port that is not listen by any app.\ndefault port range:\nnet.ipv4.ip_local_port_range = 32768\t60999\nchoose a port greater than 61000 should be less race.\n--- config\n    server_tokens off;\n    location /t {\n        set $port 65432;\n\n        content_by_lua '\n            local socket = ngx.socket\n            -- local socket = require \"socket\"\n\n            local udp = socket.udp()\n\n            local port = ngx.var.port\n            udp:settimeout(1000) -- 1 sec\n\n            local ok, err = udp:setpeername(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected\")\n\n            local req = \"\\\\0\\\\1\\\\0\\\\0\\\\0\\\\1\\\\0\\\\0flush_all\\\\r\\\\n\"\n            local ok, err = udp:send(req)\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local data, err = udp:receive()\n            if not data then\n                ngx.say(\"failed to receive data: \", err)\n                return\n            end\n            ngx.print(\"received \", #data, \" bytes: \", data)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected\nfailed to receive data: connection refused\n--- error_log eval\nqr/recv\\(\\) failed \\(\\d+: Connection refused\\)/\n\n\n\n=== TEST 4: access conflicts of connect() on shared udp objects\n--- http_config\n    lua_package_path '$prefix/html/?.lua;;';\n--- config\n    server_tokens off;\n    location /main {\n        content_by_lua '\n            local reqs = {}\n            for i = 1, 170 do\n                table.insert(reqs, {\"/t\"})\n            end\n            local resps = {ngx.location.capture_multi(reqs)}\n            for i = 1, 170 do\n                ngx.say(resps[i].status)\n            end\n        ';\n    }\n\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        #set $port 1234;\n\n        content_by_lua '\n            local port = ngx.var.port\n            local foo = require \"foo\"\n            local udp = foo.get_udp()\n\n            udp:settimeout(100) -- 100 ms\n\n            local ok, err = udp:setpeername(\"127.0.0.1\", port)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to connect: \", err)\n                return ngx.exit(500)\n            end\n\n            ngx.say(\"connected\")\n\n            local data, err = udp:receive()\n            if not data then\n                ngx.say(\"failed to receive data: \", err)\n                return\n            end\n            ngx.print(\"received \", #data, \" bytes: \", data)\n        ';\n    }\n--- user_files\n>>> foo.lua\nmodule(\"foo\", package.seeall)\n\nlocal udp\n\nfunction get_udp()\n    if not udp then\n        udp = ngx.socket.udp()\n    end\n\n    return udp\nend\n\n--- stap2\nM(http-lua-info) {\n    printf(\"udp resume: %p\\n\", $coctx)\n    print_ubacktrace()\n}\n\n--- request\nGET /main\n--- response_body_like: \\b500\\b\n--- error_log eval\nqr/content_by_lua\\(nginx\\.conf:\\d+\\):8: bad request/\n\n\n\n=== TEST 5: access conflicts of receive() on shared udp objects\n--- http_config\n    lua_package_path '$prefix/html/?.lua;;';\n--- config\n    server_tokens off;\n    location /main {\n        content_by_lua '\n            local reqs = {}\n            for i = 1, 170 do\n                table.insert(reqs, {\"/t\"})\n            end\n            local resps = {ngx.location.capture_multi(reqs)}\n            for i = 1, 170 do\n                ngx.say(resps[i].status)\n            end\n        ';\n    }\n\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        #set $port 1234;\n\n        content_by_lua '\n            local port = ngx.var.port\n            local foo = require \"foo\"\n            local udp = foo.get_udp(port)\n\n            local data, err = udp:receive()\n            if not data then\n                ngx.log(ngx.ERR, \"failed to receive data: \", err)\n                return ngx.exit(500)\n            end\n            ngx.print(\"received \", #data, \" bytes: \", data)\n        ';\n    }\n--- user_files\n>>> foo.lua\nmodule(\"foo\", package.seeall)\n\nlocal udp\n\nfunction get_udp(port)\n    if not udp then\n        udp = ngx.socket.udp()\n\n        udp:settimeout(100) -- 100ms\n\n        local ok, err = udp:setpeername(\"127.0.0.1\", port)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to connect: \", err)\n            return ngx.exit(500)\n        end\n    end\n\n    return udp\nend\n--- request\nGET /main\n--- response_body_like: \\b500\\b\n--- error_log eval\nqr/content_by_lua\\(nginx\\.conf:\\d+\\):6: bad request/\n\n\n\n=== TEST 6: connect again immediately\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.udp()\n            local port = ngx.var.port\n\n            local ok, err = sock:setpeername(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            ok, err = sock:setpeername(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected again: \", ok)\n\n            local req = \"\\\\0\\\\1\\\\0\\\\0\\\\0\\\\1\\\\0\\\\0flush_all\\\\r\\\\n\"\n            local ok, err = sock:send(req)\n            if not ok then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n            ngx.say(\"request sent: \", ok)\n\n            local line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body eval\n\"connected: 1\nconnected again: 1\nrequest sent: 1\nreceived: \\0\\1\\0\\0\\0\\1\\0\\0OK\\r\\n\nclose: 1 nil\n\"\n--- no_error_log\n[error]\n--- error_log eval\n[\"lua reuse socket upstream\", \"lua udp socket reconnect without shutting down\"]\n--- log_level: debug\n\n\n\n=== TEST 7: recv timeout\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.udp()\n            sock:settimeout(100) -- 100 ms\n\n            local ok, err = sock:setpeername(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n\n            -- ok, err = sock:close()\n            -- ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: timeout\n--- error_log\nlua udp socket read timed out\n\n\n\n=== TEST 8: with an explicit receive buffer size argument\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        #set $port 1234;\n\n        content_by_lua '\n            local socket = ngx.socket\n            -- local socket = require \"socket\"\n\n            local udp = socket.udp()\n\n            local port = ngx.var.port\n            udp:settimeout(1000) -- 1 sec\n\n            local ok, err = udp:setpeername(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected\")\n\n            local req = \"\\\\0\\\\1\\\\0\\\\0\\\\0\\\\1\\\\0\\\\0flush_all\\\\r\\\\n\"\n            local ok, err = udp:send(req)\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local data, err = udp:receive(1400)\n            if not data then\n                ngx.say(\"failed to receive data: \", err)\n                return\n            end\n            ngx.print(\"received \", #data, \" bytes: \", data)\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\n\"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}\"\n--- no_error_log\n[error]\n--- log_level: debug\n--- error_log\nlua udp socket receive buffer size: 1400\n\n\n\n=== TEST 9: read timeout and re-receive\n--- config\n    location = /t {\n        content_by_lua '\n            local udp = ngx.socket.udp()\n            udp:settimeout(30)\n            local ok, err = udp:setpeername(\"127.0.0.1\", 19232)\n            if not ok then\n                ngx.say(\"failed to setpeername: \", err)\n                return\n            end\n            local ok, err = udp:send(\"blah\")\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n            for i = 1, 2 do\n                local data, err = udp:receive()\n                if err == \"timeout\" then\n                    -- continue\n                else\n                    if not data then\n                        ngx.say(\"failed to receive: \", err)\n                        return\n                    end\n                    ngx.say(\"received: \", data)\n                    return\n                end\n            end\n\n            ngx.say(\"timed out\")\n        ';\n    }\n--- udp_listen: 19232\n--- udp_reply: hello world\n--- udp_reply_delay: 45ms\n--- request\nGET /t\n--- response_body\nreceived: hello world\n--- error_log\nlua udp socket read timed out\n\n\n\n=== TEST 10: access the google DNS server (using IP addr)\n--- config\n    server_tokens off;\n    location /t {\n        content_by_lua '\n            local socket = ngx.socket\n            -- local socket = require \"socket\"\n\n            local udp = socket.udp()\n\n            udp:settimeout(5000) -- 5 sec\n\n            local ok, err = udp:setpeername(\"$TEST_NGINX_RESOLVER\", 53)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local req = \"\\\\0}\\\\1\\\\0\\\\0\\\\1\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\3www\\\\6google\\\\3com\\\\0\\\\0\\\\1\\\\0\\\\1\"\n\n            -- ngx.print(req)\n            -- do return end\n\n            local ok, err = udp:send(req)\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local data, err = udp:receive()\n            if not data then\n                ngx.say(\"failed to receive data: \", err)\n                return\n            end\n\n            if string.match(data, \"\\\\3www\\\\6google\\\\3com\") then\n                ngx.say(\"received a good response.\")\n            else\n                ngx.say(\"received a bad response: \", #data, \" bytes: \", data)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nreceived a good response.\n--- no_error_log\n[error]\n--- log_level: debug\n--- error_log\nlua udp socket receive buffer size: 65536\n--- no_check_leak\n\n\n\n=== TEST 11: access the google DNS server (using domain names)\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            -- avoid flushing google in \"check leak\" testing mode:\n            local counter = package.loaded.counter\n            if not counter then\n                counter = 1\n            elseif counter >= 2 then\n                return ngx.exit(503)\n            else\n                counter = counter + 1\n            end\n            package.loaded.counter = counter\n\n            local socket = ngx.socket\n            -- local socket = require \"socket\"\n\n            local udp = socket.udp()\n\n            udp:settimeout(2000) -- 2 sec\n\n            local ok, err = udp:setpeername(\"google-public-dns-a.google.com\", 53)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local req = \"\\\\0}\\\\1\\\\0\\\\0\\\\1\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\3www\\\\6google\\\\3com\\\\0\\\\0\\\\1\\\\0\\\\1\"\n\n            -- ngx.print(req)\n            -- do return end\n\n            local ok, err = udp:send(req)\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local data, err = udp:receive()\n            if not data then\n                ngx.say(\"failed to receive data: \", err)\n                return\n            end\n\n            if string.match(data, \"\\\\3www\\\\6google\\\\3com\") then\n                ngx.say(\"received a good response.\")\n            else\n                ngx.say(\"received a bad response: \", #data, \" bytes: \", data)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nreceived a good response.\n--- no_error_log\n[error]\n--- log_level: debug\n--- error_log\nlua udp socket receive buffer size: 65536\n--- no_check_leak\n\n\n\n=== TEST 12: github issue #215: Handle the posted requests in lua cosocket api (failed to resolve)\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 5s;\n\n    location = /sub {\n        content_by_lua '\n            local sock = ngx.socket.udp()\n            local ok, err = sock:setpeername(\"xxx\", 80)\n            if not ok then\n                ngx.say(\"failed to connect to xxx: \", err)\n                return\n            end\n            ngx.say(\"successfully connected to xxx!\")\n            sock:close()\n        ';\n    }\n\n    location = /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /sub\n\n--- stap\nF(ngx_resolve_name_done) {\n    println(\"resolve name done\")\n}\n\n--- stap_out\nresolve name done\n\n--- response_body_like chop\n^failed to connect to xxx: xxx could not be resolved.*?Host not found\n\n--- no_error_log\n[error]\n--- timeout: 10\n\n\n\n=== TEST 13: github issue #215: Handle the posted requests in lua cosocket api (successfully resolved)\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 5s;\n\n    location = /sub {\n        content_by_lua '\n            if not package.i then\n                package.i = 1\n            end\n\n            local servers = {\"openresty.org\", \"agentzh.org\", \"sregex.org\"}\n            local server = servers[package.i]\n            package.i = package.i + 1\n\n            local sock = ngx.socket.udp()\n            local ok, err = sock:setpeername(server, 80)\n            if not ok then\n                ngx.say(\"failed to connect to \", server, \": \", err)\n                return\n            end\n            ngx.say(\"successfully connected to xxx!\")\n            sock:close()\n        ';\n    }\n\n    location = /lua {\n        content_by_lua '\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nsuccessfully connected to xxx!\n\n--- no_error_log\n[error]\n--- timeout: 10\n\n\n\n=== TEST 14: datagram unix domain socket\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        #set $port 1234;\n\n        content_by_lua '\n            local socket = ngx.socket\n            -- local socket = require \"socket\"\n\n            local udp = socket.udp()\n\n            local port = ngx.var.port\n            udp:settimeout(1000) -- 1 sec\n\n            local ok, err = udp:setpeername(\"unix:a.sock\")\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected\")\n\n            local req = \"hello,\\\\nserver\"\n            local ok, err = udp:send(req)\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local data, err = udp:receive()\n            if not data then\n                ngx.say(\"failed to receive data: \", err)\n                return\n            end\n            ngx.print(\"received \", #data, \" bytes: \", data)\n        ';\n    }\n--- request\nGET /t\n\n--- udp_listen: a.sock\n--- udp_reply\nhello,\nclient\n\n--- response_body\nconnected\nreceived 14 bytes: hello,\nclient\n\n--- stap2\nprobe syscall.socket, syscall.connect {\n    print(name, \"(\", argstr, \")\")\n}\n\nprobe syscall.socket.return, syscall.connect.return {\n    println(\" = \", retstr)\n}\n--- no_error_log\n[error]\n[crit]\n--- skip_eval: 3: $^O ne 'linux'\n\n\n\n=== TEST 15: bad request tries to setpeer\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:setpeername(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to set peer: \", err)\n                else\n                    ngx.say(\"peer set\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            sock:setpeername(\"127.0.0.1\", ngx.var.port)\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.udp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^peer set\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):14: bad request/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 16: bad request tries to send\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:setpeername(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to set peer: \", err)\n                else\n                    ngx.say(\"peer set\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            sock:send(\"a\")\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.udp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^peer set\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):14: bad request/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 17: bad request tries to receive\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:setpeername(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to set peer: \", err)\n                else\n                    ngx.say(\"peer set\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            sock:receive()\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.udp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^peer set\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):14: bad request/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 18: bad request tries to close\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    server_tokens off;\n    location = /main {\n        echo_location /t?reset=1;\n        echo_location /t;\n    }\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local test = require \"test\"\n            if ngx.var.arg_reset then\n                local sock = test.new_sock()\n                local ok, err = sock:setpeername(\"127.0.0.1\", ngx.var.port)\n                if not ok then\n                    ngx.say(\"failed to set peer: \", err)\n                else\n                    ngx.say(\"peer set\")\n                end\n                return\n            end\n            local sock = test.get_sock()\n            sock:close()\n        ';\n    }\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal sock\n\nfunction new_sock()\n    sock = ngx.socket.udp()\n    return sock\nend\n\nfunction get_sock()\n    return sock\nend\n--- request\nGET /main\n--- response_body_like eval\nqr/^peer set\n<html.*?500 Internal Server Error/ms\n\n--- error_log eval\nqr/runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):14: bad request/\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 19: the upper bound of port range should be 2^16 - 1\n--- config\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.udp()\n            local ok, err = sock:setpeername(\"127.0.0.1\", 65536)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\nfailed to connect: bad port number: 65536\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: send boolean and nil\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local socket = ngx.socket\n            local udp = socket.udp()\n            local port = ngx.var.port\n            udp:settimeout(1000) -- 1 sec\n\n            local ok, err = udp:setpeername(\"127.0.0.1\", ngx.var.port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local function send(data)\n                local bytes, err = udp:send(data)\n                if not bytes then\n                    ngx.say(\"failed to send: \", err)\n                    return\n                end\n                ngx.say(\"sent ok\")\n            end\n\n            send(true)\n            send(false)\n            send(nil)\n        }\n    }\n--- request\nGET /t\n--- response_body\nsent ok\nsent ok\nsent ok\n--- no_error_log\n[error]\n--- grep_error_log eval\nqr/send: fd:\\d+ \\d+ of \\d+/\n--- grep_error_log_out eval\nqr/send: fd:\\d+ 4 of 4\nsend: fd:\\d+ 5 of 5\nsend: fd:\\d+ 3 of 3/\n--- log_level: debug\n\n\n\n=== TEST 21: send numbers\nNote: maximum number of digits after the decimal-point character is 13\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local socket = ngx.socket\n            local udp = socket.udp()\n            local port = ngx.var.port\n            udp:settimeout(1000) -- 1 sec\n\n            local ok, err = udp:setpeername(\"127.0.0.1\", ngx.var.port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local function send(data)\n                local bytes, err = udp:send(data)\n                if not bytes then\n                    ngx.say(\"failed to send: \", err)\n                    return\n                end\n                ngx.say(\"sent ok\")\n            end\n\n            send(123456)\n            send(3.141926)\n            send(3.141592653579397238)\n        }\n    }\n--- request\nGET /t\n--- response_body\nsent ok\nsent ok\nsent ok\n--- no_error_log\n[error]\n--- grep_error_log eval\nqr/send: fd:\\d+ \\d+ of \\d+/\n--- grep_error_log_out eval\nqr/send: fd:\\d+ 6 of 6\nsend: fd:\\d+ 8 of 8\nsend: fd:\\d+ 15 of 15/\n--- log_level: debug\n\n\n\n=== TEST 22: send tables of string fragments (with numbers too)\nthe maximum number of significant digits is 14 in lua\n--- config\n    server_tokens off;\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local socket = ngx.socket\n            local udp = socket.udp()\n            local port = ngx.var.port\n            udp:settimeout(1000) -- 1 sec\n\n            local ok, err = udp:setpeername(\"127.0.0.1\", ngx.var.port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local function send(data)\n                local bytes, err = udp:send(data)\n                if not bytes then\n                    ngx.say(\"failed to send: \", err)\n                    return\n                end\n                ngx.say(\"sent ok\")\n            end\n\n            send({\"integer: \", 1234567890123})\n            send({\"float: \", 3.1419265})\n            send({\"float: \", 3.141592653579397238})\n        }\n    }\n--- request\nGET /t\n--- response_body\nsent ok\nsent ok\nsent ok\n--- no_error_log\n[error]\n--- grep_error_log eval\nqr/send: fd:\\d+ \\d+ of \\d+/\n--- grep_error_log_out eval\nqr/send: fd:\\d+ 22 of 22\nsend: fd:\\d+ 16 of 16\nsend: fd:\\d+ 22 of 22/\n--- log_level: debug\n\n\n\n=== TEST 23: udp bind\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        #set $port 1234;\n\n        content_by_lua_block {\n            local socket = ngx.socket\n            local udp = socket.udp()\n\n            local port = ngx.var.port\n            udp:settimeout(1000) -- 1 sec\n\n            local ok, err = udp:bind(\"127.0.0.10\")\n            if not ok then\n                ngx.say(\"failed to bind: \", err)\n                return\n            end\n\n            local ok, err = udp:setpeername(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected\")\n\n            local req = \"\\0\\1\\0\\0\\0\\1\\0\\0flush_all\\r\\n\"\n            local ok, err = udp:send(req)\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local data, err = udp:receive()\n            if not data then\n                ngx.say(\"failed to receive data: \", err)\n                return\n            end\n            ngx.print(\"received \", #data, \" bytes: \", data)\n        }\n    }\n--- request\nGET /t\n--- response_body eval\n\"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}\"\n--- no_error_log\n[error]\n--- log_level: debug\n--- error_log\nlua udp socket receive buffer size: 65536\n\n\n\n=== TEST 24: udp bind failed\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        #set $port 1234;\n\n        content_by_lua_block {\n            local socket = ngx.socket\n            local udp = socket.udp()\n\n            local port = ngx.var.port\n            udp:settimeout(1000) -- 1 sec\n\n            local ok, err = udp:bind(\"127.0.0.1000\")\n            if not ok then\n                ngx.say(\"failed to bind: \", err)\n                return\n            end\n\n            local ok, err = udp:setpeername(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected\")\n\n            local req = \"\\0\\1\\0\\0\\0\\1\\0\\0flush_all\\r\\n\"\n            local ok, err = udp:send(req)\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local data, err = udp:receive()\n            if not data then\n                ngx.say(\"failed to receive data: \", err)\n                return\n            end\n            ngx.print(\"received \", #data, \" bytes: \", data)\n        }\n    }\n--- request\nGET /t\n--- response_body\nfailed to bind: bad address\n--- no_error_log\n[error]\n--- log_level: debug\n"
  },
  {
    "path": "t/088-req-method.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: get method name in main request\n--- config\n    location /t {\n        content_by_lua '\n            ngx.say(\"method: [\", ngx.req.get_method(), \"]\")\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nmethod: [GET]\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: get method name in subrequest\n--- config\n    location /t {\n        echo_subrequest POST /sub;\n    }\n\n    location /sub {\n        content_by_lua '\n            ngx.say(\"method: [\", ngx.req.get_method(), \"]\")\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nmethod: [POST]\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: set GET to POST\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_method(ngx.HTTP_POST)\n        ';\n\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n\n    location /echo {\n        echo $request_method;\n    }\n--- request\nGET /t\n--- response_body\nPOST\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: set POST to GET\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_method(ngx.HTTP_GET)\n        ';\n\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n\n    location /echo {\n        echo $request_method;\n    }\n--- request\nPOST /t\nhello world\n--- response_body\nGET\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: set POST to DELETE\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_method(ngx.HTTP_DELETE)\n        ';\n\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n\n    location /echo {\n        echo $request_method;\n    }\n--- request\nPOST /t\nhello world\n--- response_body\nDELETE\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: set POST to PUT\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_method(ngx.HTTP_PUT)\n        ';\n\n        proxy_pass http://127.0.0.1:$server_port/echo;\n    }\n\n    location /echo {\n        echo $request_method;\n    }\n--- request\nPOST /t\nhello world\n--- response_body\nPUT\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: set POST to PUT (using $requeset_method)\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_method(ngx.HTTP_PUT)\n        ';\n\n        echo $request_method;\n    }\n--- request\nPOST /t\nhello world\n--- response_body\nPUT\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: set GET to HEAD\n--- no_http2\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_method(ngx.HTTP_HEAD)\n        ';\n\n        proxy_pass http://127.0.0.1:$server_port/echo;\n        #proxy_pass http://127.0.0.1:8888/;\n    }\n\n    location /echo {\n        echo $request_method;\n    }\n--- request\nGET /t\n--- response_body\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: set method name in subrequest\n--- config\n    location /t {\n        echo_subrequest POST /sub;\n        echo \"main: $echo_request_method\";\n    }\n\n    location /sub {\n        content_by_lua '\n            ngx.req.set_method(ngx.HTTP_PUT)\n            ngx.say(\"sub: \", ngx.var.echo_request_method)\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nsub: PUT\nmain: GET\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: set HEAD to GET\nXXX: does http3 do not support set HEAD to GET??\n--- no_http2\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_method(ngx.HTTP_GET)\n        ';\n\n        echo \"method: $echo_request_method\";\n    }\n--- request\n    HEAD /t\n--- response_body\nmethod: GET\n--- no_error_log\n[error]\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 11: set GET to WebDAV methods\nXXX: does http3 do not support change HEAD method?\n--- no_http2\n--- config\n    location /t {\n        content_by_lua '\n            local methods = {\n                ngx.HTTP_MKCOL,\n                ngx.HTTP_COPY,\n                ngx.HTTP_MOVE,\n                ngx.HTTP_PROPFIND,\n                ngx.HTTP_PROPPATCH,\n                ngx.HTTP_LOCK,\n                ngx.HTTP_UNLOCK,\n                ngx.HTTP_PATCH,\n                ngx.HTTP_TRACE,\n            }\n\n            for i, method in ipairs(methods) do\n                ngx.req.set_method(method)\n                ngx.say(\"method: \", ngx.var.echo_request_method)\n            end\n        ';\n    }\n--- request\n    HEAD /t\n--- response_body\nmethod: MKCOL\nmethod: COPY\nmethod: MOVE\nmethod: PROPFIND\nmethod: PROPPATCH\nmethod: LOCK\nmethod: UNLOCK\nmethod: PATCH\nmethod: TRACE\n--- no_error_log\n[error]\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n"
  },
  {
    "path": "t/089-phase.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 2) + 2;\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: get_phase in init_by_lua\n--- http_config\n    init_by_lua 'phase = ngx.get_phase()';\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(phase)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ninit\n\n\n\n=== TEST 2: get_phase in set_by_lua\n--- config\n    set_by_lua $phase 'return ngx.get_phase()';\n    location /lua {\n        content_by_lua '\n            ngx.say(ngx.var.phase)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nset\n\n\n\n=== TEST 3: get_phase in rewrite_by_lua\n--- config\n    location /lua {\n        rewrite_by_lua '\n            ngx.say(ngx.get_phase())\n            ngx.exit(200)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nrewrite\n\n\n\n=== TEST 4: get_phase in access_by_lua\n--- config\n    location /lua {\n        access_by_lua '\n            ngx.say(ngx.get_phase())\n            ngx.exit(200)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\naccess\n\n\n\n=== TEST 5: get_phase in content_by_lua\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(ngx.get_phase())\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ncontent\n\n\n\n=== TEST 6: get_phase in header_filter_by_lua\n--- config\n    location /lua {\n        echo \"OK\";\n        header_filter_by_lua '\n            ngx.header.Phase = ngx.get_phase()\n        ';\n    }\n--- request\nGET /lua\n--- response_header\nPhase: header_filter\n\n\n\n=== TEST 7: get_phase in body_filter_by_lua\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.exit(200)\n        ';\n        body_filter_by_lua '\n            ngx.arg[1] = ngx.get_phase()\n        ';\n    }\n--- request\nGET /lua\n--- response_body chop\nbody_filter\n\n\n\n=== TEST 8: get_phase in log_by_lua\n--- config\n    location /lua {\n        echo \"OK\";\n        log_by_lua '\n            ngx.log(ngx.ERR, ngx.get_phase())\n        ';\n    }\n--- request\nGET /lua\n--- error_log\nlog\n\n\n\n=== TEST 9: get_phase in ngx.timer callback\n--- config\n    location /lua {\n        echo \"OK\";\n        log_by_lua '\n            local function f()\n                ngx.log(ngx.WARN, \"current phase: \", ngx.get_phase())\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to add timer: \", err)\n            end\n        ';\n    }\n--- request\nGET /lua\n--- no_error_log\n[error]\n--- error_log\ncurrent phase: timer\n\n\n\n=== TEST 10: get_phase in init_worker_by_lua\n--- http_config\n    init_worker_by_lua 'phase = ngx.get_phase()';\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(phase)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ninit_worker\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: get_phase in exit_worker_by_lua\n--- http_config\n    exit_worker_by_lua_block {\n        local phase = ngx.get_phase()\n        ngx.log(ngx.ERR, phase)\n        ngx.log(ngx.ERR, \"exiting now\")\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\nok\n--- shutdown_error_log eval\n[\nqr/exit_worker_by_lua\\(nginx\\.conf:\\d+\\):\\d+: exit_worker/,\nqr/exiting now$/,\n]\n\n\n\n=== TEST 12: server_rewrite_by_lua_block in http\n--- http_config\n    server_rewrite_by_lua_block {\n        ngx.ctx.phase = ngx.get_phase()\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(ngx.ctx.phase)\n        }\n    }\n--- request\nGET /lua\n--- response_body\nserver_rewrite\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: precontent_by_lua_block in http\n--- config\n    location /lua {\n        precontent_by_lua_block {\n            ngx.say(ngx.get_phase())\n            ngx.exit(200)\n        }\n    }\n--- request\nGET /lua\n--- response_body\nprecontent\n"
  },
  {
    "path": "t/090-log-socket-errors.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\nlog_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: log socket errors off (tcp)\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n\n    location /t {\n        lua_socket_connect_timeout 1ms;\n        lua_socket_log_errors off;\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            ngx.say(err)\n        ';\n    }\n--- request\nGET /t\n--- response_body\ntimeout\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: log socket errors on (tcp)\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n\n    location /t {\n        lua_socket_connect_timeout 1ms;\n        lua_socket_log_errors on;\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            ngx.say(err)\n        ';\n    }\n--- request\nGET /t\n--- response_body\ntimeout\n--- error_log\nlua tcp socket connect timed out, upstream: 127.0.0.2:12345(127.0.0.2)\n\n\n\n=== TEST 3: log socket errors on (udp)\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n\n    location /t {\n        lua_socket_log_errors on;\n        lua_socket_read_timeout 1ms;\n        content_by_lua '\n            local sock = ngx.socket.udp()\n            local ok, err = sock:setpeername(\"127.0.0.2\", 12345)\n            ok, err = sock:receive()\n            ngx.say(err)\n        ';\n    }\n--- request\nGET /t\n--- response_body\ntimeout\n--- error_log\nlua udp socket read timed out\n\n\n\n=== TEST 4: log socket errors off (udp)\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n\n    location /t {\n        lua_socket_log_errors off;\n        lua_socket_read_timeout 1ms;\n        content_by_lua '\n            local sock = ngx.socket.udp()\n            local ok, err = sock:setpeername(\"127.0.0.2\", 12345)\n            ok, err = sock:receive()\n            ngx.say(err)\n        ';\n    }\n--- request\nGET /t\n--- response_body\ntimeout\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/091-coroutine.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 14);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\nour $StapScript = <<'_EOC_';\nglobal ids, cur\n\nfunction gen_id(k) {\n    if (ids[k]) return ids[k]\n    ids[k] = ++cur\n    return cur\n}\n\nF(ngx_http_handler) {\n    delete ids\n    cur = 0\n}\n\n/*\nF(ngx_http_lua_run_thread) {\n    id = gen_id($ctx->cur_co)\n    printf(\"run thread %d\\n\", id)\n}\n\nprobe process(\"/usr/local/openresty-debug/luajit/lib/libluajit-5.1.so.2\").function(\"lua_resume\") {\n    id = gen_id($L)\n    printf(\"lua resume %d\\n\", id)\n}\n*/\n\nM(http-lua-user-coroutine-resume) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"resume %x in %x\\n\", c, p)\n}\n\nM(http-lua-thread-yield) {\n    println(\"thread yield\")\n}\n\n/*\nF(ngx_http_lua_coroutine_yield) {\n    printf(\"yield %x\\n\", gen_id($L))\n}\n*/\n\nM(http-lua-user-coroutine-yield) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"yield %x in %x\\n\", c, p)\n}\n\nF(ngx_http_lua_atpanic) {\n    printf(\"lua atpanic(%d):\", gen_id($L))\n    print_ubacktrace();\n}\n\nM(http-lua-user-coroutine-create) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"create %x in %x\\n\", c, p)\n}\n\nF(ngx_http_lua_ngx_exec) { println(\"exec\") }\n\nF(ngx_http_lua_ngx_exit) { println(\"exit\") }\nF(ngx_http_lua_ffi_exit) { println(\"exit\") }\n_EOC_\n\nno_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: basic coroutine print\n--- config\n    location /lua {\n        content_by_lua '\n            local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n\n            local function f()\n                local cnt = 0\n                for i = 1, 20 do\n                    ngx.say(\"Hello, \", cnt)\n                    cy()\n                    cnt = cnt + 1\n                end\n            end\n\n            local c = cc(f)\n            for i=1,3 do\n                cr(c)\n                ngx.say(\"***\")\n            end\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- response_body\nHello, 0\n***\nHello, 1\n***\nHello, 2\n***\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: basic coroutine2\n--- config\n    location /lua {\n        content_by_lua '\n            local function f(fid)\n                local cnt = 0\n                while true do\n                    ngx.say(\"cc\", fid, \": \", cnt)\n                    coroutine.yield()\n                    cnt = cnt + 1\n                end\n            end\n\n            local ccs = {}\n            for i=1,3 do\n                ccs[#ccs+1] = coroutine.create(function() f(i) end)\n            end\n\n            for i=1,9 do\n                local cc = table.remove(ccs, 1)\n                coroutine.resume(cc)\n                ccs[#ccs+1] = cc\n            end\n        ';\n    }\n--- request\nGET /lua\n--- response_body\ncc1: 0\ncc2: 0\ncc3: 0\ncc1: 1\ncc2: 1\ncc3: 1\ncc1: 2\ncc2: 2\ncc3: 2\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: basic coroutine and cosocket\naccess the public network is unstable, need a bigger timeout \n--- quic_max_idle_timeout: 4\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /lua {\n        content_by_lua '\n            local function worker(url)\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(url, 80)\n                coroutine.yield()\n                if not ok then\n                    ngx.say(\"failed to connect to: \", url, \" error: \", err)\n                    return\n                end\n                coroutine.yield()\n                ngx.say(\"successfully connected to: \", url)\n                sock:close()\n            end\n\n            local urls = {\n                \"agentzh.org\",\n                \"openresty.com\",\n                \"openresty.org\"\n            }\n\n            local ccs = {}\n            for i, url in ipairs(urls) do\n                local cc = coroutine.create(function() worker(url) end)\n                ccs[#ccs+1] = cc\n            end\n\n            while true do\n                if #ccs == 0 then break end\n                local cc = table.remove(ccs, 1)\n                local ok = coroutine.resume(cc)\n                if ok then\n                    ccs[#ccs+1] = cc\n                end\n            end\n\n            ngx.say(\"*** All Done ***\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nsuccessfully connected to: agentzh.org\nsuccessfully connected to: openresty.com\nsuccessfully connected to: openresty.org\n*** All Done ***\n--- no_error_log\n[error]\n--- timeout: 10\n\n\n\n=== TEST 4: coroutine.wrap(generate prime numbers)\n--- config\n    location /lua {\n        content_by_lua '\n            -- generate all the numbers from 2 to n\n            local function gen (n)\n              return coroutine.wrap(function ()\n                for i=2,n do coroutine.yield(i) end\n              end)\n            end\n\n            -- filter the numbers generated by g, removing multiples of p\n            local function filter (p, g)\n              return coroutine.wrap(function ()\n                while 1 do\n                  local n = g()\n                  if n == nil then return end\n                  if math.fmod(n, p) ~= 0 then coroutine.yield(n) end\n                end\n              end)\n            end\n\n            local N = 10\n            local x = gen(N)\t\t-- generate primes up to N\n            while 1 do\n              local n = x()\t\t-- pick a number until done\n              if n == nil then break end\n              ngx.say(n)\t\t-- must be a prime number\n              x = filter(n, x)\t-- now remove its multiples\n            end\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n2\n3\n5\n7\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: coroutine.wrap(generate prime numbers,reset create and resume)\n--- config\n    location /lua {\n        content_by_lua '\n            coroutine.create = nil\n            coroutine.resume = nil\n            -- generate all the numbers from 2 to n\n            local function gen (n)\n              return coroutine.wrap(function ()\n                for i=2,n do coroutine.yield(i) end\n              end)\n            end\n\n            -- filter the numbers generated by g, removing multiples of p\n            local function filter (p, g)\n              return coroutine.wrap(function ()\n                while 1 do\n                  local n = g()\n                  if n == nil then return end\n                  if math.fmod(n, p) ~= 0 then coroutine.yield(n) end\n                end\n              end)\n            end\n\n            local N = 10 \n            local x = gen(N)\t\t-- generate primes up to N\n            while 1 do\n              local n = x()\t\t-- pick a number until done\n              if n == nil then break end\n              ngx.say(n)\t\t-- must be a prime number\n              x = filter(n, x)\t-- now remove its multiples\n            end\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n2\n3\n5\n7\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: coroutine.wrap(generate fib)\n--- config\n    location /lua {\n        content_by_lua '\n            local function generatefib (n)\n              return coroutine.wrap(function ()\n                local a,b = 1, 1\n                while a <= n do\n                  coroutine.yield(a)\n                  a, b = b, a+b\n                end\n              end)\n            end\n\n            -- In lua, because OP_TFORLOOP uses luaD_call to execute the iterator function,\n            -- and luaD_call is a C function, so we can not yield in the iterator function.\n            -- So the following case(using for loop) will be failed.\n            -- Luajit is OK.\n            if package.loaded[\"jit\"] then\n                for i in generatefib(1000) do ngx.say(i) end\n            else\n                local gen = generatefib(1000)\n                while true do\n                    local i = gen()\n                    if not i then break end\n                    ngx.say(i)\n                end\n            end\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n1\n1\n2\n3\n5\n8\n13\n21\n34\n55\n89\n144\n233\n377\n610\n987\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: coroutine wrap and cosocket\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /lua {\n        content_by_lua '\n            local function worker(url)\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(url, 80)\n                coroutine.yield()\n                if not ok then\n                    ngx.say(\"failed to connect to: \", url, \" error: \", err)\n                    return\n                end\n                coroutine.yield()\n                ngx.say(\"successfully connected to: \", url)\n                sock:close()\n            end\n\n            local urls = {\n                \"agentzh.org\",\n                \"openresty.com\",\n                \"openresty.org\"\n            }\n\n            local cfs = {}\n            for i, url in ipairs(urls) do\n                local cf = coroutine.wrap(function() worker(url) end)\n                cfs[#cfs+1] = cf\n            end\n\n            for i=1,3 do cfs[i]() end\n            for i=1,3 do cfs[i]() end\n            for i=1,3 do cfs[i]() end\n\n            ngx.say(\"*** All Done ***\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nsuccessfully connected to: agentzh.org\nsuccessfully connected to: openresty.com\nsuccessfully connected to: openresty.org\n*** All Done ***\n--- no_error_log\n[error]\n--- timeout: 10\n\n\n\n=== TEST 8: coroutine status, running\n--- config\n    location /lua {\n        content_by_lua '\n            local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n            local st, rn = coroutine.status, coroutine.running\n\n            local function f(self)\n                local cnt = 0\n                if rn() ~= self then ngx.say(\"error\"); return end\n                ngx.say(\"running: \", st(self)) --running\n                cy()\n                local c = cc(function(father)\n                    ngx.say(\"normal: \", st(father))\n                end) -- normal\n                cr(c, self)\n            end\n\n            local c = cc(f)\n            ngx.say(\"suspended: \", st(c)) -- suspended\n            cr(c, c)\n            ngx.say(\"suspended: \", st(c)) -- suspended\n            cr(c, c)\n            ngx.say(\"dead: \", st(c)) -- dead\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nsuspended: suspended\nrunning: running\nsuspended: suspended\nnormal: normal\ndead: dead\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: entry coroutine yielded will be resumed immediately\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(\"[\", {coroutine.yield()}, \"]\")\n            ngx.say(\"[\", {coroutine.yield(1, \"a\")}, \"]\")\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n[]\n[]\ndone\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: thread traceback (multi-thread)\nNote: only coroutine.wrap propagates errors to the parent coroutine\n(and thus produces a traceback)\n--- config\n    location /lua {\n        content_by_lua_block {\n            local f = function(cr) coroutine.resume(cr) end\n            -- emit a error\n            local g = function() unknown.unknown = 1 end\n            local l1 = coroutine.wrap(f)\n            local l2 = coroutine.wrap(g)\n            local l3 = coroutine.wrap(function() l1(l2) end)\n            l3()\n            ngx.say(\"hello\")\n        }\n    }\n--- request\nGET /lua\n--- error_code: 500\n--- response_body_unlike\nhello\n--- error_log eval\n[\"stack traceback:\", \"coroutine 0:\", \"coroutine 1:\", \"coroutine 2:\"]\n\n\n\n=== TEST 11: thread traceback (only the entry thread)\n--- config\n    location /lua {\n        content_by_lua '\n            -- emit a error\n            unknown.unknown = 1\n            ngx.say(\"hello\")\n        ';\n    }\n--- request\nGET /lua\n--- error_code: 500\n--- error_log eval\n[\"stack traceback:\", \"coroutine 0:\"]\n\n\n\n=== TEST 12: bug: resume dead coroutine with args\n--- config\n    location /lua {\n        content_by_lua '\n            local function print(...)\n                local args = {...}\n                local is_first = true\n                for i,v in ipairs(args) do\n                    if is_first then\n                        is_first = false\n                    else\n                        ngx.print(\" \")\n                    end\n                    ngx.print(v)\n                end\n                ngx.print(\"\\\\\\n\")\n            end\n\n            local function foo (a)\n                print(\"foo\", a)\n                return coroutine.yield(2*a)\n            end\n\n            local co = coroutine.create(function (a,b)\n                    print(\"co-body\", a, b)\n                    local r = foo(a+1)\n                    print(\"co-body\", r)\n                    local r, s = coroutine.yield(a+b, a-b)\n                    print(\"co-body\", r, s)\n                    return b, \"end\"\n                end)\n\n            print(\"main\", coroutine.resume(co, 1, 10))\n            print(\"main\", coroutine.resume(co, \"r\"))\n            print(\"main\", coroutine.resume(co, \"x\", \"y\"))\n            print(\"main\", coroutine.resume(co, \"x\", \"y\"))\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nco-body 1 10\nfoo 2\nmain true 4\nco-body r\nmain true 11 -9\nco-body x y\nmain true 10 end\nmain false cannot resume dead coroutine\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: deeply nested coroutines\n--- config\n    location /lua {\n        content_by_lua '\n            local create = coroutine.create\n            local resume = coroutine.resume\n            local yield = coroutine.yield\n            local g\n            local function f()\n                ngx.say(\"f begin\")\n                yield()\n                local c2 = create(g)\n                ngx.say(\"1: resuming c2\")\n                resume(c2)\n                ngx.say(\"2: resuming c2\")\n                resume(c2)\n                yield()\n                ngx.say(\"3: resuming c2\")\n                resume(c2)\n                ngx.say(\"f done\")\n            end\n\n            function g()\n                ngx.say(\"g begin\")\n                yield()\n                ngx.say(\"g going\")\n                yield()\n                ngx.say(\"g done\")\n            end\n\n            local c1 = create(f)\n            ngx.say(\"1: resuming c1\")\n            resume(c1)\n            ngx.say(\"2: resuming c1\")\n            resume(c1)\n            ngx.say(\"3: resuming c1\")\n            resume(c1)\n            ngx.say(\"main done\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n1: resuming c1\nf begin\n2: resuming c1\n1: resuming c2\ng begin\n2: resuming c2\ng going\n3: resuming c1\n3: resuming c2\ng done\nf done\nmain done\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: using ngx.exit in user coroutines\n--- config\n    location /lua {\n        content_by_lua '\n            local create = coroutine.create\n            local resume = coroutine.resume\n            local yield = coroutine.yield\n\n            local code = 400\n            local g\n\n            local function f()\n                local c2 = create(g)\n                yield()\n                code = code + 1\n                resume(c2)\n                yield()\n                resume(c2)\n            end\n\n            function g()\n                code = code + 1\n                yield()\n                code = code + 1\n                ngx.exit(code)\n            end\n\n            local c1 = create(f)\n            resume(c1)\n            resume(c1)\n            resume(c1)\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua\n--- stap eval: $::StapScript\n--- stap_out\ncreate 2 in 1\nresume 2 in 1\ncreate 3 in 2\nyield 2 in 1\nresume 2 in 1\nresume 3 in 2\nyield 3 in 2\nyield 2 in 1\nresume 2 in 1\nresume 3 in 2\nexit\n\n--- response_body_like: 403 Forbidden\n--- error_code: 403\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: using ngx.exec in user coroutines\n--- config\n    location /lua {\n        content_by_lua '\n            local create = coroutine.create\n            local resume = coroutine.resume\n            local yield = coroutine.yield\n\n            local code = 0\n            local g\n\n            local function f()\n                local c2 = create(g)\n                yield()\n                code = code + 1\n                resume(c2)\n                yield()\n                resume(c2)\n            end\n\n            function g()\n                code = code + 1\n                yield()\n                code = code + 1\n                ngx.exec(\"/n/\" .. code)\n            end\n\n            local c1 = create(f)\n            resume(c1)\n            resume(c1)\n            resume(c1)\n            ngx.say(\"done\")\n        ';\n    }\n\n    location ~ '^/n/(\\d+)' {\n        echo \"num: $1\";\n    }\n\n--- stap eval: $::StapScript\n--- stap_out\ncreate 2 in 1\nresume 2 in 1\ncreate 3 in 2\nyield 2 in 1\nresume 2 in 1\nresume 3 in 2\nyield 3 in 2\nyield 2 in 1\nresume 2 in 1\nresume 3 in 2\nexec\n\n--- request\nGET /lua\n--- response_body\nnum: 3\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: coroutine.create in header_filter_by_lua\n--- config\n    location /lua {\n        echo hello;\n        header_filter_by_lua '\n            local function f()\n                yield()\n            end\n\n            local c1 = coroutine.create(f)\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nAPI disabled in the context of header_filter_by_lua*\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 17: resume coroutines from within another one that is not its parent\n--- config\n    location /t {\n        content_by_lua '\n            local print = ngx.say\n\n            local c1, c2\n\n            local function f()\n                print(\"f 1\")\n                print(coroutine.resume(c2))\n                print(\"f 2\")\n            end\n\n            local function g()\n                print(\"g 1\")\n                -- print(coroutine.resume(c1))\n                print(\"g 2\")\n            end\n\n            c1 = coroutine.create(f)\n            c2 = coroutine.create(g)\n\n            coroutine.resume(c1)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nf 1\ng 1\ng 2\ntrue\nf 2\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: infinite recursive calls of coroutine.resume\n--- config\n    location /t {\n        content_by_lua '\n            local print = ngx.say\n\n            local c1, c2\n\n            local function f()\n                print(\"f 1\")\n                print(coroutine.resume(c2))\n                print(\"f 2\")\n            end\n\n            local function g()\n                print(\"g 1\")\n                print(coroutine.resume(c1))\n                print(\"g 2\")\n            end\n\n            c1 = coroutine.create(f)\n            c2 = coroutine.create(g)\n\n            coroutine.resume(c1)\n        ';\n    }\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- response_body\nf 1\ng 1\nfalsecannot resume normal coroutine\ng 2\ntrue\nf 2\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: resume running (entry) coroutines\n--- config\n    location /t {\n        content_by_lua '\n            ngx.say(coroutine.status(coroutine.running()))\n            ngx.say(coroutine.resume(coroutine.running()))\n        ';\n    }\n--- request\nGET /t\n--- response_body\nrunning\nfalsecannot resume running coroutine\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: resume running (user) coroutines\n--- config\n    location /t {\n        content_by_lua '\n            local co\n            local function f()\n                ngx.say(\"f: \", coroutine.status(co))\n                ngx.say(\"f: \", coroutine.resume(co))\n            end\n            co = coroutine.create(f)\n            ngx.say(\"chunk: \", coroutine.status(co))\n            ngx.say(\"chunk: \", coroutine.resume(co))\n        ';\n    }\n--- request\nGET /t\n--- response_body\nchunk: suspended\nf: running\nf: falsecannot resume running coroutine\nchunk: true\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: user coroutine end with errors, and the parent coroutine gets the right status\n--- config\n    location /t {\n        content_by_lua '\n            local co\n            local function f()\n                error(\"bad\")\n            end\n            co = coroutine.create(f)\n            ngx.say(\"child: resume: \", coroutine.resume(co))\n            ngx.say(\"child: status: \", coroutine.status(co))\n            ngx.say(\"parent: status: \", coroutine.status(coroutine.running()))\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\nqr/^child: resume: falsecontent_by_lua\\(nginx\\.conf:\\d+\\):4: bad\nchild: status: dead\nparent: status: running\n$/s\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: entry coroutine is yielded by hand and still gets the right status\n--- config\n    location /t {\n        content_by_lua '\n            local co = coroutine.running()\n            ngx.say(\"status: \", coroutine.status(co))\n            coroutine.yield(co)\n            ngx.say(\"status: \", coroutine.status(co))\n        ';\n    }\n--- request\nGET /t\n--- response_body\nstatus: running\nstatus: running\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: github issue #208: coroutine as iterator doesn't work\n--- config\n    location = /t {\n        content_by_lua '\n            local say = ngx.say\n            local wrap, yield = coroutine.wrap, coroutine.yield\n\n            local function it(it_state)\n              for i = 1, it_state.i do\n                yield(it_state.path, tostring(i))\n              end\n              return nil\n            end\n\n            local function it_factory(path)\n              local it_state = { i = 10, path = path }\n              return wrap(it), it_state\n            end\n\n            --[[\n            for path, value in it_factory(\"test\") do\n              say(path, value)\n            end\n            ]]\n\n            do\n              local f, s, var = it_factory(\"test\")\n              while true do\n                local path, value = f(s, var)\n                var = path\n                if var == nil then break end\n                say(path, value)\n              end\n            end\n        ';\n    }\n--- request\n    GET /t\n--- more_headers\nCookie: abc=32\n--- stap2 eval: $::StapScript\n--- response_body\ntest1\ntest2\ntest3\ntest4\ntest5\ntest6\ntest7\ntest8\ntest9\ntest10\n--- no_error_log\n[error]\n\n\n\n=== TEST 24: init_by_lua + our own coroutines in content_by_lua\n--- http_config\n    init_by_lua 'return';\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /lua {\n        content_by_lua '\n            local function worker(url)\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(url, 80)\n                coroutine.yield()\n                if not ok then\n                    ngx.say(\"failed to connect to: \", url, \" error: \", err)\n                    return\n                end\n                coroutine.yield()\n                ngx.say(\"successfully connected to: \", url)\n                sock:close()\n            end\n\n            local urls = {\n                \"agentzh.org\",\n            }\n\n            local ccs = {}\n            for i, url in ipairs(urls) do\n                local cc = coroutine.create(function() worker(url) end)\n                ccs[#ccs+1] = cc\n            end\n\n            while true do\n                if #ccs == 0 then break end\n                local cc = table.remove(ccs, 1)\n                local ok = coroutine.resume(cc)\n                if ok then\n                    ccs[#ccs+1] = cc\n                end\n            end\n\n            ngx.say(\"*** All Done ***\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nsuccessfully connected to: agentzh.org\n*** All Done ***\n--- no_error_log\n[error]\n--- timeout: 10\n\n\n\n=== TEST 25: init_by_lua_file + our own coroutines in content_by_lua\n--- http_config\n    init_by_lua_file html/init.lua;\n\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /lua {\n        content_by_lua '\n            local function worker(url)\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(url, 80)\n                coroutine.yield()\n                if not ok then\n                    ngx.say(\"failed to connect to: \", url, \" error: \", err)\n                    return\n                end\n                coroutine.yield()\n                ngx.say(\"successfully connected to: \", url)\n                sock:close()\n            end\n\n            local urls = {\n                \"agentzh.org\"\n            }\n\n            local ccs = {}\n            for i, url in ipairs(urls) do\n                local cc = coroutine.create(function() worker(url) end)\n                ccs[#ccs+1] = cc\n            end\n\n            while true do\n                if #ccs == 0 then break end\n                local cc = table.remove(ccs, 1)\n                local ok = coroutine.resume(cc)\n                if ok then\n                    ccs[#ccs+1] = cc\n                end\n            end\n\n            ngx.say(\"*** All Done ***\")\n        ';\n    }\n--- user_files\n>>> init.lua\nreturn\n\n--- request\nGET /lua\n--- response_body\nsuccessfully connected to: agentzh.org\n*** All Done ***\n--- no_error_log\n[error]\n--- timeout: 10\n\n\n\n=== TEST 26: mixing coroutine.* API between init_by_lua and other contexts (github #304) - init_by_lua\n--- http_config\n    init_by_lua '\n          co_wrap = coroutine.wrap\n          co_yield = coroutine.yield\n    ';\n\n--- config\n    location /cotest {\n        content_by_lua '\n            local function generator()\n                return co_wrap(function()\n                    co_yield(\"data\")\n                end)\n            end\n\n            local co = generator()\n            local data = co()\n            ngx.say(data)\n        ';\n    }\n\n--- request\nGET /cotest\n--- stap2 eval: $::StapScript\n--- response_body\ndata\n--- no_error_log\n[error]\n\n\n\n=== TEST 27: mixing coroutine.* API between init_by_lua and other contexts (github #304) - init_by_lua_file\n--- http_config\n    init_by_lua_file html/init.lua;\n\n--- config\n    location /cotest {\n        content_by_lua '\n            local function generator()\n                return co_wrap(function()\n                    co_yield(\"data\")\n                end)\n            end\n\n            local co = generator()\n            local data = co()\n            ngx.say(data)\n        ';\n    }\n\n--- user_files\n>>> init.lua\nco_wrap = coroutine.wrap\nco_yield = coroutine.yield\n\n--- request\nGET /cotest\n--- stap2 eval: $::StapScript\n--- response_body\ndata\n--- no_error_log\n[error]\n\n\n\n=== TEST 28: coroutine context collicisions\n--- config\n    location /lua {\n        content_by_lua '\n            local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n\n            local function f()\n                return 3\n            end\n\n            for i = 1, 10 do\n                collectgarbage()\n                local c = cc(f)\n                if coroutine.status(c) == \"dead\" then\n                    ngx.say(\"found a dead coroutine\")\n                    return\n                end\n                cr(c)\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- response_body\nok\n--- no_error_log\n[error]\n\n\n\n=== TEST 29: require \"coroutine\"\n--- config\n    location /lua {\n        content_by_lua '\n            local coroutine = require \"coroutine\"\n            local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n\n            local function f()\n                local cnt = 0\n                for i = 1, 20 do\n                    ngx.say(\"Hello, \", cnt)\n                    ngx.sleep(0.001)\n                    cy()\n                    cnt = cnt + 1\n                end\n            end\n\n            local c = cc(f)\n            for i=1,3 do\n                cr(c)\n                ngx.say(\"***\")\n            end\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- response_body\nHello, 0\n***\nHello, 1\n***\nHello, 2\n***\n--- no_error_log\n[error]\n\n\n\n=== TEST 30: basic coroutine in header_filter_by_lua\n--- config\n    location = /t {\n        echo ok;\n        header_filter_by_lua '\n            local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n\n            local function f()\n                local cnt = 0\n                for i = 1, 20 do\n                    print(\"co yield: \", cnt)\n                    cy()\n                    cnt = cnt + 1\n                end\n            end\n\n            local c = cc(f)\n            for i = 1, 3 do\n                print(\"co resume.\")\n                cr(c)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nok\n--- grep_error_log eval: qr/co (?:yield: \\d+|resume\\.)/\n--- grep_error_log_out\nco resume.\nco yield: 0\nco resume.\nco yield: 1\nco resume.\nco yield: 2\n--- no_error_log\n[error]\n\n\n\n=== TEST 31: basic coroutine in body_filter_by_lua\n--- config\n    location = /t {\n        echo ok;\n        body_filter_by_lua '\n            local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n\n            local function f()\n                local cnt = 0\n                for i = 1, 20 do\n                    print(\"co yield: \", cnt)\n                    cy()\n                    cnt = cnt + 1\n                end\n            end\n\n            local c = cc(f)\n            for i = 1, 3 do\n                print(\"co resume.\")\n                cr(c)\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body\nok\n--- grep_error_log eval: qr/co (?:yield: \\d+|resume\\.)/\n--- grep_error_log_out\nco resume.\nco yield: 0\nco resume.\nco yield: 1\nco resume.\nco yield: 2\nco resume.\nco yield: 0\nco resume.\nco yield: 1\nco resume.\nco yield: 2\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 32: coroutine.wrap propagates errors to parent coroutine\n--- config\n    location = /t {\n        content_by_lua_block {\n            local co = coroutine.wrap(function()\n                print(\"in wrapped coroutine\")\n                error(\"something went wrong\")\n            end)\n\n            co()\n\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 500\n--- response_body_unlike\nok\n--- error_log eval\n[\n    qr/\\[notice\\] .*? in wrapped coroutine/,\n    qr/\\[error\\] .*? lua entry thread aborted: runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: something went wrong/,\n    \"stack traceback:\",\n    \"coroutine 0:\",\n    \"coroutine 1:\"\n]\n\n\n\n=== TEST 33: coroutine.wrap propagates nested errors to parent coroutine\nNote: in this case, both the error message and the traceback are constructed\nfrom co1's stack level.\n--- config\n    location = /t {\n        content_by_lua_block {\n            local co1 = coroutine.wrap(function()\n                error(\"something went wrong in co1\")\n            end)\n\n            local co2 = coroutine.wrap(function()\n                co1()\n            end)\n\n            co2()\n        }\n    }\n--- request\nGET /t\n--- error_code: 500\n--- ignore_response\n--- error_log eval\n[\n    qr/\\[error\\] .*? lua entry thread aborted: runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: something went wrong in co1/,\n    \"stack traceback:\",\n    \"coroutine 0:\",\n    \"coroutine 1:\",\n    \"coroutine 2:\"\n]\n\n\n\n=== TEST 34: coroutine.wrap propagates nested errors with stack level to parent coroutine\nNote: in this case, the error message is constructed at the entry thread stack\nlevel, and the traceback is constructed from co1's stack level.\n--- config\n    location = /t {\n        content_by_lua_block {\n            local co1 = coroutine.wrap(function()\n                error(\"something went wrong in co1\", 2)\n            end)\n\n            local co2 = coroutine.wrap(function()\n                co1()\n            end)\n\n            co2()\n        }\n    }\n--- request\nGET /t\n--- error_code: 500\n--- error_log eval\n[\n    qr/\\[error\\] .*? lua entry thread aborted: runtime error: something went wrong in co1/,\n    \"stack traceback:\",\n    \"coroutine 0:\",\n    \"coroutine 1:\",\n    \"coroutine 2:\"\n]\n\n\n\n=== TEST 35: coroutine.wrap runtime errors do not log errors\n--- no_http2\n--- config\n    location = /t {\n        content_by_lua_block {\n            local co = coroutine.wrap(function()\n                print(\"in wrapped coroutine\")\n                error(\"something went wrong\")\n            end)\n\n            co()\n        }\n    }\n--- request\nGET /t\n--- error_code: 500\n--- ignore_response\n--- no_error_log eval\n[\n    qr/\\[error\\] .*? lua coroutine: runtime error:/,\n    \"[crit]\",\n    \"[emerg]\",\n    \"[warn]\",\n]\n\n\n\n=== TEST 36: coroutine.wrap does not return status boolean on yield\n--- config\n    location = /t {\n        content_by_lua_block {\n            local co = coroutine.wrap(function()\n                coroutine.yield(\"ok\", \"err\")\n            end)\n\n            local ret1, ret2 = co()\n\n            ngx.say(ret1, \", \", ret2)\n        }\n    }\n--- request\nGET /t\n--- response_body\nok, err\n--- no_error_log\n[error]\n\n\n\n=== TEST 37: coroutine.wrap does not return status boolean on done\n--- config\n    location = /t {\n        content_by_lua_block {\n            local co = coroutine.wrap(function() end)\n\n            local ret1 = co()\n\n            ngx.say(ret1)\n        }\n    }\n--- request\nGET /t\n--- response_body\nnil\n--- no_error_log\n[error]g\n\n\n\n=== TEST 38: coroutine.wrap does not return status boolean on error\n--- SKIP: not supported\n--- config\n    location = /t {\n        content_by_lua_block {\n            local co = coroutine.wrap(function()\n                error(\"something went wrong\")\n            end)\n\n            local ret1, ret2 = pcall(co)\n\n            ngx.say(ret1, \", \", ret2)\n        }\n    }\n--- request\nGET /t\n--- response_body\nfalse, something went wrong\n--- no_error_log\n[error]\n\n\n\n=== TEST 39: coroutine.wrap creates different function refs\n--- config\n    location = /t {\n        content_by_lua_block {\n            local f = function() end\n            local co = coroutine.wrap(f)\n            local co2 = coroutine.wrap(f)\n\n            ngx.say(\"co == co2: \", co == co2)\n        }\n    }\n--- request\nGET /t\n--- response_body\nco == co2: false\n--- no_error_log\n[error]\n\n\n\n=== TEST 40: coroutine.wrap supports yielding and resuming\n--- config\n    location = /t {\n        content_by_lua_block {\n            local function f()\n                local cnt = 0\n                for i = 1, 20 do\n                    ngx.say(\"co yield: \", cnt)\n                    coroutine.yield()\n                    cnt = cnt + 1\n                end\n            end\n\n            local f = coroutine.wrap(f)\n            for i = 1, 3 do\n                ngx.say(\"co resume\")\n                f()\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\nco resume\nco yield: 0\nco resume\nco yield: 1\nco resume\nco yield: 2\n--- no_error_log\n[error]\n\n\n\n=== TEST 41: coroutine.wrap return values\n--- config\n    location = /t {\n        content_by_lua_block {\n            local function f()\n                local cnt = 0\n                for i = 1, 20 do\n                    coroutine.yield(cnt, cnt + 1)\n                    cnt = cnt + 1\n                end\n            end\n\n            local f = coroutine.wrap(f)\n            for i = 1, 3 do\n                ngx.say(\"co resume\")\n                local ret1, ret2 = f()\n                ngx.say(\"co yield: \", ret1, \", \", ret2)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\nco resume\nco yield: 0, 1\nco resume\nco yield: 1, 2\nco resume\nco yield: 2, 3\n--- no_error_log\n[error]\n\n\n\n=== TEST 42: coroutine.wrap arguments\n--- config\n    location = /t {\n        content_by_lua_block {\n            local function f(step)\n                local cnt = 0\n                for i = 1, 20 do\n                    ngx.say(\"co yield: \", cnt)\n                    coroutine.yield()\n                    cnt = cnt + step\n                end\n            end\n\n            local f = coroutine.wrap(f)\n            for i = 1, 3 do\n                ngx.say(\"co resume\")\n                f(i)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\nco resume\nco yield: 0\nco resume\nco yield: 1\nco resume\nco yield: 2\n--- no_error_log\n[error]\n\n\n\n=== TEST 43: coroutine.wrap in header_filter_by_lua (orig coroutine.wrap)\n--- config\n    location = /t {\n        return 200;\n\n        header_filter_by_lua_block {\n            local function f()\n                local cnt = 0\n                for i = 1, 20 do\n                    print(\"co yield: \", cnt)\n                    coroutine.yield()\n                    cnt = cnt + 1\n                end\n            end\n\n            local f = coroutine.wrap(f)\n            for i = 1, 3 do\n                print(\"co resume.\")\n                f()\n            end\n        }\n    }\n--- request\nGET /t\n--- ignore_response\n--- grep_error_log eval: qr/co (?:yield: \\d+|resume\\.)/\n--- grep_error_log_out\nco resume.\nco yield: 0\nco resume.\nco yield: 1\nco resume.\nco yield: 2\n--- no_error_log\n[error]\n\n\n\n=== TEST 44: coroutine.wrap in header_filter_by_lua propagates errors (orig coroutine.wrap)\n--- config\n    location = /t {\n        return 200;\n\n        header_filter_by_lua_block {\n            local co = coroutine.wrap(function()\n                print(\"in wrapped coroutine\")\n                error(\"something went wrong\")\n            end)\n\n            local err = co()\n\n            ngx.log(ngx.CRIT, \"err: \", err)\n        }\n    }\n--- request\nGET /t\n--- ignore_response\n--- error_log eval\n[\n    qr/\\[notice\\] .*? in wrapped coroutine/,\n    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/,\n    \"stack traceback:\",\n    \"in function 'co'\"\n]\n--- curl_error eval\nqr/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/\n\n\n\n=== TEST 45: coroutine.wrap in init_by_lua propagates errors (orig coroutine.wrap)\n--- http_config\n    init_by_lua_block {\n        local co = coroutine.wrap(function()\n            print(\"in wrapped coroutine\")\n            error(\"something went wrong\")\n        end)\n\n        local err = co()\n\n        ngx.log(ngx.CRIT, \"err: \", err)\n    }\n--- config\n\n--- must_die\n--- grep_error_log eval: qr/init_by_lua\\(nginx.conf:25\\).*? something went wrong/\n--- grep_error_log_out\ninit_by_lua(nginx.conf:25):7: init_by_lua(nginx.conf:25):4: something went wrong\n\n\n\n=== TEST 46: coroutine.resume runtime errors do not log errors\n--- config\n    location = /t {\n        content_by_lua_block {\n            local function f()\n                error(\"something went wrong\")\n            end\n\n            local ret1, ret2 = coroutine.resume(coroutine.create(f))\n            ngx.say(ret1)\n            ngx.say(ret2)\n        }\n    }\n--- request\nGET /t\n--- response_body_like\nfalse\ncontent_by_lua\\(nginx.conf:\\d+\\):\\d+: something went wrong\n--- no_error_log eval\n[\n    qr/\\[error\\] .*? lua coroutine: runtime error:\",\n    \"stack traceback:\",\n]\n"
  },
  {
    "path": "t/092-eof.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 6);\n\nmaster_on();\nworkers(2);\nno_root_location();\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: 404 parallel subrequests after ngx.eof()\n--- config\n    location = /lua {\n        content_by_lua '\n            ngx.say(1)\n            ngx.eof()\n            local res1, res2 = ngx.location.capture_multi{\n                { \"/bad1\" },\n                { \"/bad2\" }\n            }\n            ngx.log(ngx.WARN, \"res1: \", res1.status)\n            ngx.log(ngx.WARN, \"res2: \", res2.status)\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n1\n--- no_error_log\n[alert]\n--- error_log\nres1: 404\nres2: 404\nNo such file or directory\n\n\n\n=== TEST 2: parallel normal subrequests after ngx.eof()\n--- config\n    location = /t {\n        content_by_lua '\n            ngx.say(1)\n            ngx.eof()\n            local r1, r2 = ngx.location.capture_multi{\n                { \"/proxy/tom\" },\n                { \"/proxy/jim\" }\n            }\n            ngx.log(ngx.WARN, r1.body)\n            ngx.log(ngx.WARN, r2.body)\n        ';\n    }\n\n    location ~ '^/proxy/(\\w+)' {\n        proxy_pass http://127.0.0.1:$server_port/hello?a=$1;\n    }\n\n    location = /hello {\n        echo_sleep 0.5;\n        echo -n \"hello, $arg_a\";\n    }\n--- request\nGET /t\n--- response_body\n1\n--- no_error_log\n[alert]\n[error]\n--- error_log\nhello, tom\nhello, jim\n"
  },
  {
    "path": "t/093-uthread-spawn.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n\n#no_shuffle();\nworker_connections(256);\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple user thread without I/O\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: two simple user threads without I/O\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"in thread 1\")\n            end\n\n            local function g()\n                ngx.say(\"in thread 2\")\n            end\n\n            ngx.say(\"before 1\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after 1\")\n\n            ngx.say(\"before 2\")\n            ngx.thread.spawn(g)\n            ngx.say(\"after 2\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 3: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 3\ndelete thread 1\n\n--- response_body\nbefore 1\nin thread 1\nafter 1\nbefore 2\nin thread 2\nafter 2\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: simple user thread with sleep\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"before sleep\")\n                ngx.sleep(0.1)\n                ngx.say(\"after sleep\")\n            end\n\n            ngx.say(\"before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread create\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore thread create\nbefore sleep\nafter thread create\nafter sleep\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: two simple user threads with sleep\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"1: before sleep\")\n                ngx.sleep(0.2)\n                ngx.say(\"1: after sleep\")\n            end\n\n            local function g()\n                ngx.say(\"2: before sleep\")\n                ngx.sleep(0.1)\n                ngx.say(\"2: after sleep\")\n            end\n\n            ngx.say(\"1: before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"1: after thread create\")\n\n            ngx.say(\"2: before thread create\")\n            ngx.thread.spawn(g)\n            ngx.say(\"2: after thread create\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 3: ok\ndelete thread 3\nterminate 2: ok\ndelete thread 2\n\n--- wait: 0.1\n--- response_body\n1: before thread create\n1: before sleep\n1: after thread create\n2: before thread create\n2: before sleep\n2: after thread create\n2: after sleep\n1: after sleep\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: error in user thread\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.blah()\n            end\n\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: fail\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nafter\n--- error_log eval\nqr/lua user thread aborted: runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: attempt to call field 'blah' \\(a nil value\\)/\n\n\n\n=== TEST 6: simple user threads doing a single subrequest (entry quits early)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"before capture\")\n                local res = ngx.location.capture(\"/proxy\")\n                ngx.say(\"after capture: \", res.body)\n            end\n\n            ngx.say(\"before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread create\")\n        ';\n    }\n\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/foo;\n    }\n\n    location /foo {\n        echo_sleep 0.1;\n        echo -n hello world;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore thread create\nbefore capture\nafter thread create\nafter capture: hello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: simple user threads doing a single subrequest (entry also does a subrequest and quits early)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"before capture\")\n                local res = ngx.location.capture(\"/proxy?foo\")\n                ngx.say(\"after capture: \", res.body)\n            end\n\n            ngx.say(\"before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread create\")\n            local res = ngx.location.capture(\"/proxy?bar\")\n            ngx.say(\"capture: \", res.body)\n        ';\n    }\n\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/$args;\n    }\n\n    location /foo {\n        echo_sleep 0.1;\n        echo -n hello foo;\n    }\n\n    location /bar {\n        echo -n hello bar;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore thread create\nbefore capture\nafter thread create\ncapture: hello bar\nafter capture: hello foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: simple user threads doing a single subrequest (entry also does a subrequest and quits late)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"before capture\")\n                local res = ngx.location.capture(\"/proxy?foo\")\n                ngx.say(\"after capture: \", res.body)\n            end\n\n            ngx.say(\"before thread create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread create\")\n            local res = ngx.location.capture(\"/proxy?bar\")\n            ngx.say(\"capture: \", res.body)\n        ';\n    }\n\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/$args;\n    }\n\n    location /foo {\n        echo_sleep 0.1;\n        echo -n hello foo;\n    }\n\n    location /bar {\n        echo_sleep 0.2;\n        echo -n hello bar;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nbefore thread create\nbefore capture\nafter thread create\nafter capture: hello foo\ncapture: hello bar\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: two simple user threads doing single subrequests (entry also does a subrequest and quits between)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"f: before capture\")\n                local res = ngx.location.capture(\"/proxy?foo\")\n                ngx.say(\"f: after capture: \", res.body)\n            end\n\n            local function g()\n                ngx.say(\"g: before capture\")\n                local res = ngx.location.capture(\"/proxy?bah\")\n                ngx.say(\"g: after capture: \", res.body)\n            end\n\n            ngx.say(\"before thread 1 create\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after thread 1 create\")\n\n            ngx.say(\"before thread 2 create\")\n            ngx.thread.spawn(g)\n            ngx.say(\"after thread 2 create\")\n\n            local res = ngx.location.capture(\"/proxy?bar\")\n            ngx.say(\"capture: \", res.body)\n        ';\n    }\n\n    location /proxy {\n        proxy_pass http://127.0.0.1:$server_port/$args;\n    }\n\n    location /foo {\n        echo_sleep 0.1;\n        echo -n hello foo;\n    }\n\n    location /bar {\n        echo_sleep 0.2;\n        echo -n hello bar;\n    }\n\n    location /bah {\n        echo_sleep 0.3;\n        echo -n hello bah;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nbefore thread 1 create\nf: before capture\nafter thread 1 create\nbefore thread 2 create\ng: before capture\nafter thread 2 create\nf: after capture: hello foo\ncapture: hello bar\ng: after capture: hello bah\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: nested user threads\n--- config\n    location /lua {\n        content_by_lua '\n            local g\n            local function f()\n                ngx.say(\"before g\")\n                ngx.thread.spawn(g)\n                ngx.say(\"after g\")\n            end\n\n            function g()\n                ngx.say(\"hello in g()\")\n            end\n\n            ngx.say(\"before f\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after f\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 3: ok\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 3\ndelete thread 2\n\n--- response_body\nbefore f\nbefore g\nhello in g()\nafter f\nafter g\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: nested user threads (with I/O)\n--- config\n    location /lua {\n        content_by_lua '\n            local g\n            local function f()\n                ngx.say(\"before g\")\n                ngx.thread.spawn(g)\n                ngx.say(\"after g\")\n            end\n\n            function g()\n                ngx.sleep(0.1)\n                ngx.say(\"hello in g()\")\n            end\n\n            ngx.say(\"before f\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after f\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nbefore f\nbefore g\nafter f\nafter g\nhello in g()\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: coroutine status of a running user thread\n--- config\n    location /lua {\n        content_by_lua '\n            local co\n            local function f()\n                co = coroutine.running()\n                ngx.sleep(0.1)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.say(\"status: \", coroutine.status(co))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nstatus: running\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: coroutine status of a dead user thread\n--- config\n    location /lua {\n        content_by_lua '\n            local co\n            local function f()\n                co = coroutine.running()\n            end\n\n            ngx.thread.spawn(f)\n            ngx.say(\"status: \", coroutine.status(co))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nstatus: zombie\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: coroutine status of a \"normal\" user thread\n--- config\n    location /lua {\n        content_by_lua '\n            local co\n            local g\n            local function f()\n                co = coroutine.running()\n                local co2 = coroutine.create(g)\n                coroutine.resume(co2)\n            end\n\n            function g()\n                ngx.sleep(0.1)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.say(\"status: \", coroutine.status(co))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 2\nterminate 1: ok\ndelete thread 1\nterminate 3: ok\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nstatus: normal\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: creating user threads in a user coroutine\n--- config\n    location /lua {\n        content_by_lua '\n            local g\n            local function f()\n                ngx.say(\"before g\")\n                ngx.thread.spawn(g)\n                ngx.say(\"after g\")\n            end\n\n            function g()\n                ngx.say(\"hello in g()\")\n            end\n\n            ngx.say(\"before f\")\n            local co = coroutine.create(f)\n            coroutine.resume(co)\n            ngx.say(\"after f\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 3: ok\nterminate 2: ok\ndelete thread 3\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nbefore f\nbefore g\nhello in g()\nafter g\nafter f\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: manual time slicing between a user thread and the entry thread\n--- config\n    location /lua {\n        content_by_lua '\n            local yield = coroutine.yield\n\n            local function f()\n                local self = coroutine.running()\n                ngx.say(\"f 1\")\n                yield(self)\n                ngx.say(\"f 2\")\n                yield(self)\n                ngx.say(\"f 3\")\n            end\n\n            local self = coroutine.running()\n            ngx.say(\"0\")\n            yield(self)\n            ngx.say(\"1\")\n            ngx.thread.spawn(f)\n            ngx.say(\"2\")\n            yield(self)\n            ngx.say(\"3\")\n            yield(self)\n            ngx.say(\"4\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\n0\n1\nf 1\n2\nf 2\n3\nf 3\n4\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: manual time slicing between two user threads\n--- config\n    location /lua {\n        content_by_lua '\n            local yield = coroutine.yield\n\n            local function f()\n                local self = coroutine.running()\n                ngx.say(\"f 1\")\n                yield(self)\n                ngx.say(\"f 2\")\n                yield(self)\n                ngx.say(\"f 3\")\n            end\n\n            local function g()\n                local self = coroutine.running()\n                ngx.say(\"g 1\")\n                yield(self)\n                ngx.say(\"g 2\")\n                yield(self)\n                ngx.say(\"g 3\")\n            end\n\n            ngx.thread.spawn(f)\n            ngx.thread.spawn(g)\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nf 1\ng 1\nf 2\ndone\ng 2\nf 3\ng 3\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: entry thread and a user thread flushing at the same time\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                coroutine.yield(coroutine.running)\n                ngx.flush(true)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            ngx.flush(true)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: two user threads flushing at the same time\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello from f\")\n                ngx.flush(true)\n            end\n\n            local function g()\n                ngx.say(\"hello from g\")\n                ngx.flush(true)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.thread.spawn(g)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like\n^(?:create 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3|create 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 3: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 3\ndelete thread 1)$\n\n--- response_body\nhello from f\nhello from g\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: user threads + ngx.socket.tcp\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n                local bytes, err = sock:send(\"flush_all\\\\r\\\\n\")\n                if not bytes then\n                    ngx.say(\"failed to send query: \", err)\n                    return\n                end\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nbefore\nafter\nreceived: OK\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: user threads + ngx.socket.udp\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                local sock = ngx.socket.udp()\n                local ok, err = sock:setpeername(\"127.0.0.1\", $TEST_NGINX_RAND_PORT_1)\n                local bytes, err = sock:send(\"blah\")\n                if not bytes then\n                    ngx.say(\"failed to send query: \", err)\n                    return\n                end\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like chop\n^(?:create 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n|create 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n)$\n\n--- udp_listen: $TEST_NGINX_RAND_PORT_1\n--- udp_query: blah\n--- udp_reply: hello udp\n--- response_body_like chop\n^(?:before\nafter\nreceived: hello udp\n|before\nreceived: hello udp\nafter)$\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: simple user thread with ngx.req.read_body()\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.req.read_body()\n                local body = ngx.req.get_body_data()\n                ngx.say(\"body: \", body)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nPOST /lua\nhello world\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like chop\n^(?:create 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1|create 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2)$\n\n--- response_body_like chop\n^(?:before\nbody: hello world\nafter|before\nafter\nbody: hello world)$\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: simple user thread with ngx.req.socket()\nngx.req.socket() does not support in http3\n--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3}\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                local sock = ngx.req.socket()\n                local body, err = sock:receive(11)\n                if not body then\n                    ngx.say(\"failed to read body: \", err)\n                    return\n                end\n\n                ngx.say(\"body: \", body)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nPOST /lua\nhello world\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like chop\n^(?:create 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1|create 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2)$\n\n--- response_body_like chop\n^(?:before\nbody: hello world\nafter|before\nafter\nbody: hello world)$\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 24: simple user thread with args\n--- config\n    location /lua {\n        content_by_lua '\n            local function f(a, b)\n                ngx.say(\"hello \", a, \" and \", b)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f, \"foo\", 3.14)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nbefore\nhello foo and 3.14\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 25: multiple user threads + subrequests returning 404 immediately\n--- config\n    location /t {\n        content_by_lua '\n            local capture = ngx.location.capture\n            local insert = table.insert\n\n            local function f(i)\n                local res = capture(\"/proxy/\" .. i)\n                ngx.say(\"status: \", res.status)\n            end\n\n            local threads = {}\n            for i = 1, 2 do\n                local co = ngx.thread.spawn(f, i)\n                insert(threads, co)\n            end\n\n            ngx.say(\"ok\")\n        ';\n    }\n\n    location ~ ^/proxy/(\\d+) {\n        return 404;\n    }\n--- request\n    GET /t\n--- stap2 eval: $::StapScript\n--- stap eval\n\"$::GCScript\"\n.\n'\nF(ngx_http_finalize_request) {\n    printf(\"finalize request %s: rc:%d c:%d a:%d\\n\", ngx_http_req_uri($r), $rc, $r->main->count, $r == $r->main);\n    #if ($rc == -1) {\n        #print_ubacktrace()\n    #}\n}\n\nM(http-subrequest-done) {\n    printf(\"subrequest %s done\\n\", ngx_http_req_uri($r))\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: %s rc=%d, status=%d a=%d\\n\", ngx_http_req_uri($r), $rc,\n         $r->headers_out->status, $r == $r->main)\n    #print_ubacktrace()\n}\n'\n--- stap_out_like chop\n^create 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nfinalize request /t: rc:-4 c:4 a:1\nfinalize request /proxy/1: rc:404 c:3 a:0\npost subreq: /proxy/1 rc=404, status=0 a=0\nsubrequest /proxy/1 done\nterminate 2: ok\ndelete thread 2\nfinalize request /proxy/2: rc:404 c:2 a:0\npost subreq: /proxy/2 rc=404, status=0 a=0\nsubrequest /proxy/2 done\nterminate 3: ok\ndelete thread 3\nfinalize request /t: rc:0 c:1 a:1\n(?:finalize request /t: rc:0 c:1 a:1)?$\n\n--- response_body\nok\nstatus: 404\nstatus: 404\n--- no_error_log\n[error]\n--- timeout: 3\n\n\n\n=== TEST 26: multiple user threads + subrequests returning 404 remotely (no wait)\n--- config\n    location /t {\n        content_by_lua '\n            local capture = ngx.location.capture\n            local insert = table.insert\n\n            local function f(i)\n                local res = capture(\"/proxy/\" .. i)\n                ngx.say(\"status: \", res.status)\n            end\n\n            local threads = {}\n            for i = 1, 5 do\n                local co = ngx.thread.spawn(f, i)\n                insert(threads, co)\n            end\n\n            ngx.say(\"ok\")\n        ';\n    }\n\n    location ~ ^/proxy/(\\d+) {\n        proxy_pass http://127.0.0.1:$server_port/d/$1;\n    }\n\n    location /d {\n        return 404;\n        #echo $uri;\n    }\n--- request\n    GET /t\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like chop\n^create 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\ncreate 4 in 1\nspawn user thread 4 in 1\ncreate 5 in 1\nspawn user thread 5 in 1\ncreate 6 in 1\nspawn user thread 6 in 1\nterminate 1: ok\ndelete thread 1\n(?:terminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\nterminate 4: ok\ndelete thread 4\nterminate 5: ok\ndelete thread 5\nterminate 6: ok\ndelete thread 6\n|terminate 6: ok\ndelete thread 6\nterminate 5: ok\ndelete thread 5\nterminate 4: ok\ndelete thread 4\nterminate 3: ok\ndelete thread 3\nterminate 2: ok\ndelete thread 2)$\n\n--- response_body\nok\nstatus: 404\nstatus: 404\nstatus: 404\nstatus: 404\nstatus: 404\n--- no_error_log\n[error]\n--- timeout: 6\n\n\n\n=== TEST 27: multiple user threads + subrequests returning 201 immediately\n--- config\n    location /t {\n        content_by_lua '\n            local capture = ngx.location.capture\n            local insert = table.insert\n\n            local function f(i)\n                local res = capture(\"/proxy/\" .. i)\n                ngx.say(\"status: \", res.status)\n            end\n\n            local threads = {}\n            for i = 1, 2 do\n                local co = ngx.thread.spawn(f, i)\n                insert(threads, co)\n            end\n\n            ngx.say(\"ok\")\n        ';\n    }\n\n    location ~ ^/proxy/(\\d+) {\n        content_by_lua 'ngx.exit(201)';\n    }\n--- request\n    GET /t\n--- stap2 eval: $::StapScript\n--- stap eval\n\"$::GCScript\"\n.\n'\nF(ngx_http_finalize_request) {\n    printf(\"finalize request %s: rc:%d c:%d a:%d\\n\", ngx_http_req_uri($r), $rc, $r->main->count, $r == $r->main);\n    #if ($rc == -1) {\n        #print_ubacktrace()\n    #}\n}\n\nM(http-subrequest-done) {\n    printf(\"subrequest %s done\\n\", ngx_http_req_uri($r))\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: %s rc=%d, status=%d a=%d\\n\", ngx_http_req_uri($r), $rc,\n         $r->headers_out->status, $r == $r->main)\n    #print_ubacktrace()\n}\n'\n\n--- stap_out_like chop\n^create 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nfinalize request /t: rc:-4 c:4 a:1\nterminate 4: ok\ndelete thread 4\nfinalize request /proxy/1: rc:0 c:3 a:0\npost subreq: /proxy/1 rc=0, status=201 a=0\nsubrequest /proxy/1 done\nterminate 2: ok\ndelete thread 2\nterminate 5: ok\ndelete thread 5\nfinalize request /proxy/2: rc:0 c:2 a:0\npost subreq: /proxy/2 rc=0, status=201 a=0\nsubrequest /proxy/2 done\nterminate 3: ok\ndelete thread 3\nfinalize request /t: rc:0 c:1 a:1\n(?:finalize request /t: rc:0 c:1 a:1)?$\n\n--- response_body\nok\nstatus: 201\nstatus: 201\n--- no_error_log\n[error]\n--- timeout: 3\n\n\n\n=== TEST 28: multiple user threads + subrequests returning 204 immediately\n--- config\n    location /t {\n        content_by_lua '\n            local capture = ngx.location.capture\n            local insert = table.insert\n\n            local function f(i)\n                local res = capture(\"/proxy/\" .. i)\n                ngx.say(\"status: \", res.status)\n            end\n\n            local threads = {}\n            for i = 1, 2 do\n                local co = ngx.thread.spawn(f, i)\n                insert(threads, co)\n            end\n\n            ngx.say(\"ok\")\n        ';\n    }\n\n    location ~ ^/proxy/(\\d+) {\n        content_by_lua 'ngx.exit(204)';\n    }\n--- request\n    GET /t\n--- stap2 eval: $::StapScript\n--- stap eval\n\"$::GCScript\"\n.\n'\nF(ngx_http_finalize_request) {\n    printf(\"finalize request %s: rc:%d c:%d a:%d\\n\", ngx_http_req_uri($r), $rc, $r->main->count, $r == $r->main);\n    #if ($rc == -1) {\n        #print_ubacktrace()\n    #}\n}\n\nM(http-subrequest-done) {\n    printf(\"subrequest %s done\\n\", ngx_http_req_uri($r))\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: %s rc=%d, status=%d a=%d\\n\", ngx_http_req_uri($r), $rc,\n         $r->headers_out->status, $r == $r->main)\n    #print_ubacktrace()\n}\n'\n--- stap_out_like chop\n^create 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nfinalize request /t: rc:-4 c:4 a:1\nterminate 4: ok\ndelete thread 4\nfinalize request /proxy/1: rc:204 c:3 a:0\npost subreq: /proxy/1 rc=204, status=204 a=0\nsubrequest /proxy/1 done\nterminate 2: ok\ndelete thread 2\nterminate 5: ok\ndelete thread 5\nfinalize request /proxy/2: rc:204 c:2 a:0\npost subreq: /proxy/2 rc=204, status=204 a=0\nsubrequest /proxy/2 done\nterminate 3: ok\ndelete thread 3\nfinalize request /t: rc:0 c:1 a:1\n(?:finalize request /t: rc:0 c:1 a:1)?$\n\n--- response_body\nok\nstatus: 204\nstatus: 204\n--- no_error_log\n[error]\n--- timeout: 3\n\n\n\n=== TEST 29: multiple user threads + subrequests returning 404 remotely (wait)\n--- config\n    location /t {\n        content_by_lua '\n            local n = 5\n            local capture = ngx.location.capture\n            local insert = table.insert\n\n            local function f(i)\n                local res = capture(\"/proxy/\" .. i)\n                return res.status\n            end\n\n            local threads = {}\n            for i = 1, n do\n                local co = ngx.thread.spawn(f, i)\n                insert(threads, co)\n            end\n\n            for i = 1, n do\n                local ok, res = ngx.thread.wait(threads[i])\n                ngx.say(i, \": \", res)\n            end\n\n            ngx.say(\"ok\")\n        ';\n    }\n\n    location ~ ^/proxy/(\\d+) {\n        proxy_pass http://127.0.0.1:$server_port/d/$1;\n    }\n\n    location /d {\n        return 404;\n        #echo $uri;\n    }\n--- request\n    GET /t\n--- stap2 eval: $::StapScript\n--- stap3 eval: $::GCScript\n--- stap_out3\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\ncreate 4 in 1\nspawn user thread 4 in 1\ncreate 5 in 1\nspawn user thread 5 in 1\ncreate 6 in 1\nspawn user thread 6 in 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\nterminate 4: ok\ndelete thread 4\nterminate 5: ok\ndelete thread 5\nterminate 6: ok\ndelete thread 6\nterminate 1: ok\ndelete thread 1\n\n--- response_body\n1: 404\n2: 404\n3: 404\n4: 404\n5: 404\nok\n--- no_error_log\n[error]\n--- timeout: 6\n\n\n\n=== TEST 30: multiple user threads + subrequests remotely (wait)\n--- config\n    location /t {\n        content_by_lua '\n            local n = 20\n            local capture = ngx.location.capture\n            local insert = table.insert\n\n            local function f(i)\n                local res = capture(\"/proxy/\" .. i)\n                return res.status\n            end\n\n            local threads = {}\n            for i = 1, n do\n                local co = ngx.thread.spawn(f, i)\n                insert(threads, co)\n            end\n\n            for i = 1, n do\n                local ok, res = ngx.thread.wait(threads[i])\n                ngx.say(i, \": \", res)\n            end\n\n            ngx.say(\"ok\")\n        ';\n    }\n\n    location ~ ^/proxy/(\\d+) {\n        proxy_pass http://127.0.0.1:$server_port/d/$1;\n    }\n\n    location /d {\n        echo_sleep 0.001;\n        echo $uri;\n    }\n--- request\n    GET /t\n--- stap2 eval: $::StapScript\n--- stap3 eval: $::GCScript\n--- stap_out3\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\ncreate 4 in 1\nspawn user thread 4 in 1\ncreate 5 in 1\nspawn user thread 5 in 1\ncreate 6 in 1\nspawn user thread 6 in 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\nterminate 4: ok\ndelete thread 4\nterminate 5: ok\ndelete thread 5\nterminate 6: ok\ndelete thread 6\nterminate 1: ok\ndelete thread 1\n\n--- response_body\n1: 200\n2: 200\n3: 200\n4: 200\n5: 200\n6: 200\n7: 200\n8: 200\n9: 200\n10: 200\n11: 200\n12: 200\n13: 200\n14: 200\n15: 200\n16: 200\n17: 200\n18: 200\n19: 200\n20: 200\nok\n--- no_error_log\n[error]\n[alert]\n--- timeout: 10\n\n\n\n=== TEST 31: simple user thread without I/O\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.say(\"f\")\n            end\n\n            ngx.thread.spawn(f)\n            collectgarbage()\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nf\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/094-uthread-exit.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4 - 2);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: exit in user thread (entry thread is still pending to run)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            ngx.sleep(1)\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nM(timer-add) {\n    if ($arg2 == 1000) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nbefore\nhello in thread\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: exit in user thread (entry thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            ngx.sleep(1)\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 1000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: exit in a user thread (another user thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.say(\"f\")\n                ngx.exit(0)\n            end\n\n            local function g()\n                ngx.sleep(1)\n                ngx.say(\"g\")\n            end\n\n            ngx.thread.spawn(f)\n            ngx.thread.spawn(g)\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\ncreate 3 in 1\nspawn user thread 3 in 1\nadd timer 1000\nterminate 1: ok\ndelete thread 1\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 3\nfree request\n\n--- response_body\nend\nf\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: exit in user thread (entry already quits)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.say(\"exiting the user thread\")\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- wait: 0.1\n--- response_body\nbefore\nafter\nexiting the user thread\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: exit in user thread (entry thread is still pending on the DNS resolver for ngx.socket.tcp)\n--- config\n    location /lua {\n        resolver 127.0.0.2:12345;\n        resolver_timeout 12s;\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.001)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"agentzh.org\", 80)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nF(ngx_resolve_name) {\n    printf(\"resolving %s\\n\", user_string_n($ctx->name->data, $ctx->name->len))\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 1) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 1) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 12000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 1) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_tcp_resolve_cleanup) {\n    println(\"lua tcp resolve cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 1\nresolving agentzh.org\nadd timer 12000\nexpire timer 1\nterminate 2: ok\ndelete thread 2\nlua tcp resolve cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: exit in user thread (entry thread is still pending on the DNS resolver for ngx.socket.udp)\n--- config\n    location /lua {\n        resolver 127.0.0.2:12345;\n        resolver_timeout 12s;\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.001)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.udp()\n            local ok, err = sock:setpeername(\"agentzh.org\", 80)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nF(ngx_resolve_name) {\n    printf(\"resolving %s\\n\", user_string_n($ctx->name->data, $ctx->name->len))\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 1) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 1) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 12000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 1) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_udp_resolve_cleanup) {\n    println(\"lua udp resolve cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 1\nresolving agentzh.org\nadd timer 12000\nexpire timer 1\nterminate 2: ok\ndelete thread 2\nlua udp resolve cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: exit in user thread (entry thread is still pending on tcpsock:connect)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.tcp()\n            sock:settimeout(12000)\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 12000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua tcp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: exit in user thread (entry thread is still pending on tcpsock:receive)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, ok = sock:send(\"blpop not_exists 2\\\\r\\\\n\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            sock:settimeout(12000)\n\n            local data, err = sock:receive()\n            if not data then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua tcp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: exit in user thread (entry thread is still pending on tcpsock:receiveuntil's iterator)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local bytes, ok = sock:send(\"blpop not_exists 2\\\\r\\\\n\")\n            if not bytes then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local it, err = sock:receiveuntil(\"\\\\r\\\\n\")\n            if not it then\n                ngx.say(\"failed to receive until: \", err)\n                return\n            end\n\n            sock:settimeout(12000)\n\n            local data, err = it()\n            if not data then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua tcp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: exit in user thread (entry thread is still pending on udpsock:receive)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.socket.udp()\n\n            local ok, err = sock:setpeername(\"8.8.8.8\", 12345)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            sock:settimeout(12000)\n\n            local data, err = sock:receive()\n            if not data then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_udp_socket_cleanup) {\n    println(\"lua udp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua udp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: exit in user thread (entry thread is still pending on reqsock:receive)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n            local sock = ngx.req.socket()\n\n            sock:settimeout(12000)\n\n            local data, err = sock:receive(1024)\n            if not data then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nPOST /lua\n--- more_headers\nContent-Length: 1024\n\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_coctx_cleanup) {\n    println(\"lua tcp socket cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua tcp socket cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 12: exit in user thread (entry thread is still pending on ngx.req.read_body)\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n\n            ngx.req.read_body()\n\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nPOST /lua\n--- more_headers\nContent-Length: 1024\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 12000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 12000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_req_body_cleanup) {\n    println(\"lua req body cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 12000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua req body cleanup\ndelete timer 12000\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nbefore\nhello in thread\nafter\n--- no_error_log\n[error]\n--- skip_eval: 4:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 13: exit(0) in user thread (entry thread is still pending on ngx.location.capture), with pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- ignore_response\n--- error_log\nattempt to abort with pending subrequests\n--- no_error_log\n[alert]\n\n\n\n=== TEST 14: exit in user thread (entry thread is still pending on ngx.location.capture), without pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.location.capture(\"/sleep\")\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq %s\\n\", ngx_http_req_uri($r))\n}\n\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\npost subreq /sleep\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nend\n--- error_log\nattempt to abort with pending subrequests\n\n\n\n=== TEST 15: exit in user thread (entry thread is still pending on ngx.location.capture_multi), without pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exit(0)\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.location.capture_multi{\n                {\"/echo\"},\n                {\"/sleep\"}\n            }\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /echo {\n        echo hello;\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq %s\\n\", ngx_http_req_uri($r))\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\npost subreq /echo\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\npost subreq /sleep\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nend\n--- error_log\nattempt to abort with pending subrequests\n\n\n\n=== TEST 16: exit in entry thread (user thread is still pending on ngx.location.capture_multi), without pending output\n--- no_http2\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        content_by_lua '\n            local function f()\n                ngx.location.capture_multi{\n                    {\"/echo\"},\n                    {\"/sleep\"}\n                }\n                ngx.say(\"end\")\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.sleep(0.1)\n            ngx.exit(0)\n        ';\n    }\n\n    location = /echo {\n        echo hello;\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- more_headers\nContent-Length: 1024\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq %s\\n\", ngx_http_req_uri($r))\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\npost subreq /echo\nadd timer 200\nexpire timer 100\nterminate 1: fail\ndelete thread 2\ndelete thread 1\ndelete timer 200\nfree request\n\n--- wait: 0.1\n--- ignore_response\n--- error_log\nattempt to abort with pending subrequests\n--- no_error_log\n[alert]\n[warn]\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP/3 stream 0 reset by server#\n\n\n\n=== TEST 17: exit(444) in user thread (entry thread is still pending on ngx.location.capture), with pending output\n--- no_http2\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(444)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- more_headers\nContent-Length: 1024\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: ok\ndelete thread 2\ndelete thread 1\ndelete timer 200\nfree request\n\n--- wait: 0.1\n--- ignore_response\n--- no_error_log\n[alert]\n[error]\n[warn]\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP/3 stream 0 reset by server#\n\n\n\n=== TEST 18: exit(408) in user thread (entry thread is still pending on ngx.location.capture), with pending output\n--- no_http2\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(408)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- more_headers\nContent-Length: 1024\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: ok\ndelete thread 2\ndelete thread 1\ndelete timer 200\nfree request\n\n--- wait: 0.1\n--- ignore_response\n--- no_error_log\n[alert]\n[error]\n[warn]\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP/3 stream 0 reset by server#\n\n\n\n=== TEST 19: exit(499) in user thread (entry thread is still pending on ngx.location.capture), with pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                ngx.sleep(0.1)\n                ngx.exit(499)\n            end\n\n            ngx.say(\"before\")\n            ngx.thread.spawn(f)\n            ngx.say(\"after\")\n\n            ngx.location.capture(\"/sleep\")\n\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- more_headers\nContent-Length: 1024\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: ok\ndelete thread 2\ndelete thread 1\ndelete timer 200\nfree request\n\n--- wait: 0.1\n--- ignore_response\n--- no_error_log\n[alert]\n[error]\n\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP/3 stream 0 reset by server#\n"
  },
  {
    "path": "t/095-uthread-exec.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: exec in user thread (entry still pending)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.exec(\"/foo\")\n            end\n\n            ngx.thread.spawn(f)\n            ngx.sleep(1)\n            ngx.say(\"hello\")\n        ';\n    }\n\n    location /foo {\n        echo i am foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\ni am foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: exec in user thread (entry already quits)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exec(\"/foo\")\n            end\n\n            ngx.thread.spawn(f)\n        ';\n    }\n\n    location /foo {\n        echo i am foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\ni am foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: exec in user thread (entry thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exec(\"/foo\")\n            end\n\n            ngx.thread.spawn(f)\n            ngx.sleep(1)\n        ';\n    }\n\n    location = /foo {\n        echo hello foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 1000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 1\nfree request\n\n--- response_body\nhello foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: exec in a user thread (another user thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exec(\"/foo\")\n            end\n\n            local function g()\n                ngx.sleep(1)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.thread.spawn(g)\n        ';\n    }\n\n    location = /foo {\n        echo hello foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\ncreate 3 in 1\nspawn user thread 3 in 1\nadd timer 1000\nterminate 1: ok\ndelete thread 1\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 3\nfree request\n\n--- wait: 0.1\n--- response_body\nhello foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: exec in user thread (entry thread is still pending on ngx.location.capture), without pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.exec(\"/foo\")\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.location.capture(\"/sleep\")\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n\n    location = /foo {\n        echo hello world;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nend\n--- error_log\nattempt to abort with pending subrequests\n\n\n\n=== TEST 6: exec in entry thread (user thread is still pending on ngx.location.capture), without pending output\n--- no_http2\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        content_by_lua '\n            local function f()\n                ngx.location.capture(\"/sleep\")\n                ngx.say(\"end\")\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.sleep(0.1)\n            ngx.exec(\"/foo\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n\n    location = /foo {\n        echo hello world;\n    }\n--- request\nPOST /lua\n--- more_headers\nContent-Length: 1024\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 1: fail\ndelete thread 2\ndelete thread 1\ndelete timer 200\nfree request\n\n--- ignore_response\n--- error_log\nattempt to abort with pending subrequests\n--- no_error_log\n[alert]\n[warn]\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP/3 stream 0 reset by server#\n"
  },
  {
    "path": "t/096-uthread-redirect.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: ngx.redirect() in user thread (entry thread is still pending on ngx.location.capture_multi), without pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.redirect(301)\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.location.capture_multi{\n                {\"/echo\"},\n                {\"/sleep\"}\n            }\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /echo {\n        echo hello;\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq %s\\n\", ngx_http_req_uri($r))\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\npost subreq /echo\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\npost subreq /sleep\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- wait: 0.1\n--- response_body\nend\n--- error_log\nattempt to abort with pending subrequests\n\n\n\n=== TEST 2: redirect in user thread (entry thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.redirect(301)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.sleep(1)\n            ngx.say(\"end\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 1000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 1\nfree request\n\n--- response_body_like: 302 Found\n--- error_code: 302\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: ngx.redirect() in entry thread (user thread is still pending on ngx.location.capture_multi), without pending output\n--- no_http2\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        content_by_lua '\n            local function f()\n                ngx.location.capture_multi{\n                    {\"/echo\"},\n                    {\"/sleep\"}\n                }\n                ngx.say(\"end\")\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.sleep(0.1)\n            ngx.redirect(301)\n        ';\n    }\n\n    location = /echo {\n        echo hello;\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n--- request\nPOST /lua\n--- more_headers\nContent-Length: 1024\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq %s\\n\", ngx_http_req_uri($r))\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\npost subreq /echo\nadd timer 200\nexpire timer 100\nterminate 1: fail\ndelete thread 2\ndelete thread 1\ndelete timer 200\nfree request\n\n--- ignore_response\n--- error_log\nattempt to abort with pending subrequests\n--- no_error_log\n[alert]\n[warn]\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP/3 stream 0 reset by server#\n"
  },
  {
    "path": "t/097-uthread-rewrite.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: rewrite in user thread (entry still pending)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.req.set_uri(\"/foo\", true)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.sleep(1)\n            ngx.say(\"hello\")\n        ';\n    }\n\n    location /foo {\n        echo i am foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\ni am foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: rewrite in user thread (entry already quits)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.req.set_uri(\"/foo\", true)\n            end\n\n            ngx.thread.spawn(f)\n        ';\n    }\n\n    location /foo {\n        echo i am foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\ni am foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: rewrite in user thread (entry thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.req.set_uri(\"/foo\", true)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.sleep(1)\n        ';\n    }\n\n    location = /foo {\n        echo hello foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 1000\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 1\nfree request\n\n--- response_body\nhello foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: rewrite in a user thread (another user thread is still pending on ngx.sleep)\n--- config\n    location /lua {\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.req.set_uri(\"/foo\", true)\n            end\n\n            local function g()\n                ngx.sleep(1)\n            end\n\n            ngx.thread.spawn(f)\n            ngx.thread.spawn(g)\n        ';\n    }\n\n    location = /foo {\n        echo hello foo;\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\ncreate 3 in 1\nspawn user thread 3 in 1\nadd timer 1000\nterminate 1: ok\ndelete thread 1\nexpire timer 100\nterminate 2: ok\ndelete thread 2\nlua sleep cleanup\ndelete timer 1000\ndelete thread 3\nfree request\n\n--- response_body\nhello foo\n--- no_error_log\n[error]\n--- wait: 0.1\n\n\n\n=== TEST 5: rewrite in user thread (entry thread is still pending on ngx.location.capture), without pending output\n--- config\n    location /lua {\n        client_body_timeout 12000ms;\n        rewrite_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.req.set_uri(\"/foo\", true)\n            end\n\n            ngx.thread.spawn(f)\n\n            ngx.location.capture(\"/sleep\")\n            ngx.say(\"end\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.2;\n    }\n\n    location = /foo {\n        echo hello world;\n    }\n--- request\nPOST /lua\n--- stap2 eval: $::StapScript\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 200 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 200 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq %s\\n\", ngx_http_req_uri($r))\n}\n\n_EOC_\n\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nadd timer 100\nadd timer 200\nexpire timer 100\nterminate 2: fail\nexpire timer 200\npost subreq /sleep\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nfree request\n\n--- response_body\nend\n--- error_log\nattempt to abort with pending subrequests\n"
  },
  {
    "path": "t/098-uthread-wait.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple user thread wait without I/O\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                return \"done\"\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            collectgarbage()\n\n            local ok, res = ngx.thread.wait(t)\n            if not ok then\n                ngx.say(\"failed to run thread: \", res)\n                return\n            end\n\n            ngx.say(res)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nhello in thread\nthread created: zombie\ndone\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: simple user thread wait with I/O\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.say(\"hello in thread\")\n                return \"done\"\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            local ok, res = ngx.thread.wait(t)\n            if not ok then\n                ngx.say(\"failed to wait thread: \", res)\n                return\n            end\n\n            ngx.say(res)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nthread created: running\nhello in thread\ndone\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: wait on uthreads on the reversed order of their termination\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.say(\"f: hello\")\n                return \"done\"\n            end\n\n            local function g()\n                ngx.sleep(0.2)\n                ngx.say(\"g: hello\")\n                return \"done\"\n            end\n\n            local tf, err = ngx.thread.spawn(f)\n            if not tf then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"f thread created: \", coroutine.status(tf))\n\n            local tg, err = ngx.thread.spawn(g)\n            if not tg then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"g thread created: \", coroutine.status(tg))\n\n            local ok, res = ngx.thread.wait(tg)\n            if not ok then\n                ngx.say(\"failed to wait g: \", res)\n                return\n            end\n\n            ngx.say(\"g: \", res)\n\n            ngx.say(\"f thread status: \", coroutine.status(tf))\n\n            ok, res = ngx.thread.wait(tf)\n            if not ok then\n                ngx.say(\"failed to wait f: \", res)\n                return\n            end\n\n            ngx.say(\"f: \", res)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 2: ok\nterminate 3: ok\ndelete thread 3\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nf thread created: running\ng thread created: running\nf: hello\ng: hello\ng: done\nf thread status: zombie\nf: done\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: wait on uthreads on the exact order of their termination\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.say(\"f: hello\")\n                return \"done\"\n            end\n\n            local function g()\n                ngx.sleep(0.2)\n                ngx.say(\"g: hello\")\n                return \"done\"\n            end\n\n            local tf, err = ngx.thread.spawn(f)\n            if not tf then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"f thread created: \", coroutine.status(tf))\n\n            local tg, err = ngx.thread.spawn(g)\n            if not tg then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"g thread created: \", coroutine.status(tg))\n\n            local ok, res = ngx.thread.wait(tf)\n            if not ok then\n                ngx.say(\"failed to wait f: \", res)\n                return\n            end\n\n            ngx.say(\"f: \", res)\n\n            ngx.say(\"g thread status: \", coroutine.status(tg))\n\n            local ok, res = ngx.thread.wait(tg)\n            if not ok then\n                ngx.say(\"failed to wait g: \", res)\n                return\n            end\n\n            ngx.say(\"g: \", res)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\nterminate 1: ok\ndelete thread 1\n\n--- wait: 0.1\n--- response_body\nf thread created: running\ng thread created: running\nf: hello\nf: done\ng thread status: running\ng: hello\ng: done\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: simple user thread wait without I/O (return multiple values)\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                return \"done\", 3.14\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            collectgarbage()\n\n            local ok, res1, res2 = ngx.thread.wait(t)\n            if not ok then\n                ngx.say(\"failed to run thread: \", res1)\n                return\n            end\n\n            ngx.say(\"res: \", res1, \" \", res2)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nhello in thread\nthread created: zombie\nres: done 3.14\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: simple user thread wait with I/O, return multiple values\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.say(\"hello in thread\")\n                return \"done\", 3.14\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            local ok, res1, res2 = ngx.thread.wait(t)\n            if not ok then\n                ngx.say(\"failed to wait thread: \", res1)\n                return\n            end\n\n            ngx.say(\"res: \", res1, \" \", res2)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nthread created: running\nhello in thread\nres: done 3.14\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: simple user thread wait without I/O, throw errors\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                error(\"bad bad!\")\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            collectgarbage()\n\n            local ok, res = ngx.thread.wait(t)\n            if not ok then\n                ngx.say(\"failed to wait thread: \", res)\n                return\n            end\n\n            ngx.say(res)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: fail\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nhello in thread\nthread created: zombie\nfailed to wait thread: bad bad!\n--- error_log eval\nqr/lua user thread aborted: runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):4: bad bad!/\n\n\n\n=== TEST 8: simple user thread wait with I/O, throw errors\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.say(\"hello in thread\")\n                error(\"bad bad!\")\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            collectgarbage()\n\n            local ok, res = ngx.thread.wait(t)\n            if not ok then\n                ngx.say(\"failed to wait thread: \", res)\n                return\n            end\n\n            ngx.say(res)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: fail\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nthread created: running\nhello in thread\nfailed to wait thread: bad bad!\n--- error_log eval\nqr/lua user thread aborted: runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):5: bad bad!/\n\n\n\n=== TEST 9: simple user thread wait without I/O (in a user coroutine)\n--- config\n    location /lua {\n        content_by_lua '\n            local function g()\n                ngx.say(\"hello in thread\")\n                return \"done\"\n            end\n\n            local function f()\n                local t, err = ngx.thread.spawn(g)\n                if not t then\n                    ngx.say(\"failed to spawn thread: \", err)\n                    return\n                end\n\n                ngx.say(\"thread created: \", coroutine.status(t))\n\n                collectgarbage()\n\n                local ok, res = ngx.thread.wait(t)\n                if not ok then\n                    ngx.say(\"failed to run thread: \", res)\n                    return\n                end\n\n                ngx.say(res)\n            end\n\n            local co = coroutine.create(f)\n            coroutine.resume(co)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 3: ok\ndelete thread 3\nterminate 2: ok\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nhello in thread\nthread created: zombie\ndone\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: simple user thread wait with I/O (in a user coroutine)\n--- config\n    location /lua {\n        content_by_lua '\n            local function g()\n                ngx.sleep(0.1)\n                ngx.say(\"hello in thread\")\n                return \"done\"\n            end\n\n            local function f()\n                local t, err = ngx.thread.spawn(g)\n                if not t then\n                    ngx.say(\"failed to spawn thread: \", err)\n                    return\n                end\n\n                ngx.say(\"thread created: \", coroutine.status(t))\n\n                collectgarbage()\n\n                local ok, res = ngx.thread.wait(t)\n                if not ok then\n                    ngx.say(\"failed to run thread: \", res)\n                    return\n                end\n\n                ngx.say(res)\n            end\n\n            local co = coroutine.create(f)\n            coroutine.resume(co)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 3: ok\ndelete thread 3\nterminate 2: ok\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nthread created: running\nhello in thread\ndone\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: waiting on two simple user threads without I/O\n--- config\n    location /lua {\n        content_by_lua '\n            -- local out = function (...) ngx.log(ngx.ERR, ...) end\n            local out = ngx.say\n\n            local function f()\n                out(\"f: hello\")\n                return \"f done\"\n            end\n\n            local function g()\n                out(\"g: hello\")\n                return \"g done\"\n            end\n\n            local tf, err = ngx.thread.spawn(f)\n            if not tf then\n                out(\"failed to spawn thread f: \", err)\n                return\n            end\n\n            out(\"thread f created: \", coroutine.status(tf))\n\n            local tg, err = ngx.thread.spawn(g)\n            if not tg then\n                out(\"failed to spawn thread g: \", err)\n                return\n            end\n\n            out(\"thread g created: \", coroutine.status(tg))\n\n            local ok, res = ngx.thread.wait(tf, tg)\n            if not ok then\n                out(\"failed to wait thread: \", res)\n                return\n            end\n\n            out(\"res: \", res)\n\n            out(\"f status: \", coroutine.status(tf))\n            out(\"g status: \", coroutine.status(tg))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 3: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 3\ndelete thread 1\n\n--- response_body\nf: hello\nthread f created: zombie\ng: hello\nthread g created: zombie\nres: f done\nf status: dead\ng status: zombie\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: waiting on two simple user threads with I/O\n--- config\n    location /lua {\n        content_by_lua '\n            -- local out = function (...) ngx.log(ngx.ERR, ...) end\n            local out = ngx.say\n\n            local function f()\n                ngx.sleep(0.1)\n                out(\"f: hello\")\n                return \"f done\"\n            end\n\n            local function g()\n                ngx.sleep(0.2)\n                out(\"g: hello\")\n                return \"g done\"\n            end\n\n            local tf, err = ngx.thread.spawn(f)\n            if not tf then\n                out(\"failed to spawn thread f: \", err)\n                return\n            end\n\n            out(\"thread f created: \", coroutine.status(tf))\n\n            local tg, err = ngx.thread.spawn(g)\n            if not tg then\n                out(\"failed to spawn thread g: \", err)\n                return\n            end\n\n            out(\"thread g created: \", coroutine.status(tg))\n\n            local ok, res = ngx.thread.wait(tf, tg)\n            if not ok then\n                out(\"failed to wait thread: \", res)\n                return\n            end\n\n            out(\"res: \", res)\n\n            out(\"f status: \", coroutine.status(tf))\n            out(\"g status: \", coroutine.status(tg))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 1\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nthread f created: running\nthread g created: running\nf: hello\nres: f done\nf status: dead\ng status: running\ng: hello\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: waiting on two simple user threads with I/O (uthreads completed in reversed order)\n--- config\n    location /lua {\n        content_by_lua '\n            -- local out = function (...) ngx.log(ngx.ERR, ...) end\n            local out = ngx.say\n\n            local function f()\n                ngx.sleep(0.2)\n                out(\"f: hello\")\n                return \"f done\"\n            end\n\n            local function g()\n                ngx.sleep(0.1)\n                out(\"g: hello\")\n                return \"g done\"\n            end\n\n            local tf, err = ngx.thread.spawn(f)\n            if not tf then\n                out(\"failed to spawn thread f: \", err)\n                return\n            end\n\n            out(\"thread f created: \", coroutine.status(tf))\n\n            local tg, err = ngx.thread.spawn(g)\n            if not tg then\n                out(\"failed to spawn thread g: \", err)\n                return\n            end\n\n            out(\"thread g created: \", coroutine.status(tg))\n\n            local ok, res = ngx.thread.wait(tf, tg)\n            if not ok then\n                out(\"failed to wait thread: \", res)\n                return\n            end\n\n            out(\"res: \", res)\n\n            out(\"f status: \", coroutine.status(tf))\n            out(\"g status: \", coroutine.status(tg))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 3: ok\ndelete thread 3\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nthread f created: running\nthread g created: running\ng: hello\nres: g done\nf status: running\ng status: dead\nf: hello\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: waiting on two simple user threads without I/O, both aborted by errors\n--- config\n    location /lua {\n        content_by_lua '\n            -- local out = function (...) ngx.log(ngx.ERR, ...) end\n            local out = ngx.say\n\n            local function f()\n                out(\"f: hello\")\n                error(\"f done\")\n            end\n\n            local function g()\n                out(\"g: hello\")\n                error(\"g done\")\n            end\n\n            local tf, err = ngx.thread.spawn(f)\n            if not tf then\n                out(\"failed to spawn thread f: \", err)\n                return\n            end\n\n            out(\"thread f created: \", coroutine.status(tf))\n\n            local tg, err = ngx.thread.spawn(g)\n            if not tg then\n                out(\"failed to spawn thread g: \", err)\n                return\n            end\n\n            out(\"thread g created: \", coroutine.status(tg))\n\n            local ok, res = ngx.thread.wait(tf, tg)\n            if not ok then\n                out(\"failed to wait thread: \", res)\n            else\n                out(\"res: \", res)\n            end\n\n            out(\"f status: \", coroutine.status(tf))\n            out(\"g status: \", coroutine.status(tg))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: fail\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 3: fail\ndelete thread 2\nterminate 1: ok\ndelete thread 3\ndelete thread 1\n\n--- response_body\nf: hello\nthread f created: zombie\ng: hello\nthread g created: zombie\nfailed to wait thread: f done\nf status: dead\ng status: zombie\n\n--- error_log eval\nqr/lua user thread aborted: runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):7: f done/\n\n\n\n=== TEST 15: waiting on two simple user threads with I/O, both aborted by errors\n--- config\n    location /lua {\n        content_by_lua '\n            -- local out = function (...) ngx.log(ngx.ERR, ...) end\n            local out = ngx.say\n\n            local function f()\n                ngx.sleep(0.1)\n                out(\"f: hello\")\n                error(\"f done\")\n            end\n\n            local function g()\n                ngx.sleep(0.2)\n                out(\"g: hello\")\n                error(\"g done\")\n            end\n\n            local tf, err = ngx.thread.spawn(f)\n            if not tf then\n                out(\"failed to spawn thread f: \", err)\n                return\n            end\n\n            out(\"thread f created: \", coroutine.status(tf))\n\n            local tg, err = ngx.thread.spawn(g)\n            if not tg then\n                out(\"failed to spawn thread g: \", err)\n                return\n            end\n\n            out(\"thread g created: \", coroutine.status(tg))\n\n            local ok, res = ngx.thread.wait(tf, tg)\n            if not ok then\n                out(\"failed to wait thread: \", res)\n            else\n                out(\"res: \", res)\n            end\n\n            out(\"f status: \", coroutine.status(tf))\n            out(\"g status: \", coroutine.status(tg))\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 2: fail\ndelete thread 2\nterminate 1: ok\ndelete thread 1\nterminate 3: fail\ndelete thread 3\n\n--- response_body\nthread f created: running\nthread g created: running\nf: hello\nfailed to wait thread: f done\nf status: dead\ng status: running\ng: hello\n\n--- error_log eval\nqr/lua user thread aborted: runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):8: f done/\n\n\n\n=== TEST 16: wait on uthreads on the exact order of their termination, but exit the world early\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                ngx.say(\"f: hello\")\n                return \"done\"\n            end\n\n            local function g()\n                ngx.sleep(0.2)\n                ngx.say(\"g: hello\")\n                return \"done\"\n            end\n\n            local tf, err = ngx.thread.spawn(f)\n            if not tf then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"f thread created: \", coroutine.status(tf))\n\n            local tg, err = ngx.thread.spawn(g)\n            if not tg then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"g thread created: \", coroutine.status(tg))\n\n            local ok, res = ngx.thread.wait(tf, tg)\n            if not ok then\n                ngx.say(\"failed to wait: \", res)\n                return\n            end\n\n            ngx.say(\"res: \", res)\n\n            ngx.exit(200)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 3\ndelete thread 1\n\n--- response_body\nf thread created: running\ng thread created: running\nf: hello\nres: done\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: wait on uthreads on the reversed order of their termination, but exit the world early\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.2)\n                ngx.say(\"f: hello\")\n                return \"f done\"\n            end\n\n            local function g()\n                ngx.sleep(0.1)\n                ngx.say(\"g: hello\")\n                return \"g done\"\n            end\n\n            local tf, err = ngx.thread.spawn(f)\n            if not tf then\n                ngx.say(\"failed to spawn thread f: \", err)\n                return\n            end\n\n            ngx.say(\"f thread created: \", coroutine.status(tf))\n\n            local tg, err = ngx.thread.spawn(g)\n            if not tg then\n                ngx.say(\"failed to spawn thread g: \", err)\n                return\n            end\n\n            ngx.say(\"g thread created: \", coroutine.status(tg))\n\n            local ok, res = ngx.thread.wait(tf, tg)\n            if not ok then\n                ngx.say(\"failed to wait: \", res)\n                return\n            end\n\n            ngx.say(\"res: \", res)\n\n            ngx.exit(200)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 3: ok\ndelete thread 3\nterminate 1: ok\ndelete thread 2\ndelete thread 1\n\n--- response_body\nf thread created: running\ng thread created: running\ng: hello\nres: g done\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: entry coroutine waiting on a thread not created by itself\n--- config\n    location /lua {\n        content_by_lua '\n            local t\n\n            local function f()\n                ngx.sleep(0.1)\n                return \"done\"\n            end\n\n            local function g()\n                t = ngx.thread.spawn(f)\n            end\n\n            local co = coroutine.create(g)\n            coroutine.resume(co)\n\n            local ok, res = ngx.thread.wait(t)\n            if not ok then\n                ngx.say(\"failed to run thread: \", res)\n                return\n            end\n\n            ngx.say(res)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 2: ok\nterminate 1: fail\ndelete thread 3\ndelete thread 1\n\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nonly the parent coroutine can wait on the thread\n\n\n\n=== TEST 19: entry coroutine waiting on a user coroutine\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                coroutine.yield()\n                return \"done\"\n            end\n\n            local co = coroutine.create(f)\n            coroutine.resume(co)\n\n            local ok, res = ngx.thread.wait(co)\n            if not ok then\n                ngx.say(\"failed to run thread: \", res)\n                return\n            end\n\n            ngx.say(res)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: fail\ndelete thread 1\n\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/lua entry thread aborted: runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):11: attempt to wait on a coroutine that is not a user thread/\n\n\n\n=== TEST 20: lua backtrace dumper may access dead parent coroutines\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.sleep(0.1)\n                collectgarbage()\n                error(\"f done\")\n            end\n\n            ngx.thread.spawn(f)\n            ngx.say(\"ok\")\n\n            collectgarbage()\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: fail\ndelete thread 2\n\n--- response_body\nok\n\n--- error_log eval\nqr/lua user thread aborted: runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):5: f done/\n\n\n\n=== TEST 21: waiting on a dead coroutine\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello in thread\")\n                return \"done\"\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            collectgarbage()\n\n            local ok, res = ngx.thread.wait(t)\n            if not ok then\n                ngx.say(\"failed to run thread: \", res)\n                return\n            end\n\n            local ok, res = ngx.thread.wait(t)\n            if not ok then\n                ngx.say(\"failed to run thread: \", res)\n                return\n            end\n\n            ngx.say(res)\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nhello in thread\nthread created: zombie\nfailed to run thread: already waited or killed\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: spawn and wait uthreads for many times\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                -- ngx.say(\"hello in thread\")\n                return \"done\"\n            end\n\n            for i = 1, 100 do\n                local t, err = ngx.thread.spawn(f)\n                if not t then\n                    ngx.say(\"failed to spawn thread: \", err)\n                    break\n                end\n\n                -- ngx.say(\"thread created: \", coroutine.status(t))\n\n                collectgarbage()\n\n                local ok, res = ngx.thread.wait(t)\n                if not ok then\n                    ngx.say(\"failed to run thread: \", res)\n                    break\n                end\n\n                ngx.say(i, \": \", res)\n            end\n        ';\n    }\n--- request\nGET /lua\n--- response_body eval\nmy $s = '';\nfor my $i (1..100) {\n    $s .= \"$i: done\\n\";\n}\n$s;\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 23: no parameters for ngx.thread.wait\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.thread.wait()\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /lua\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nat least one coroutine should be specified\n--- no_error_log\n[crit]\n"
  },
  {
    "path": "t/099-c-api.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(3);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\nno_long_string();\n#master_on();\n#workers(2);\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: find zone\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local ffi = require \"ffi\"\n\n            ffi.cdef[[\n                void *ngx_http_lua_find_zone(char *data, size_t len);\n            ]]\n\n            local buf = ffi.new(\"char[?]\", 4)\n            ffi.copy(buf, \"foo\", 3)\n            local zone = ffi.C.ngx_http_lua_find_zone(buf, 3)\n            ngx.say(\"foo zone: \", tonumber(ffi.cast(\"long\", zone)) ~= 0 and \"defined\" or \"undef\")\n\n            ffi.copy(buf, \"dogs\", 4)\n            zone = ffi.C.ngx_http_lua_find_zone(buf, 4)\n            ngx.say(\"dogs zone: \", tonumber(ffi.cast(\"long\", zone)) ~= 0 and \"defined\" or \"undef\")\n        ';\n    }\n--- request\nGET /test\n--- response_body\nfoo zone: undef\ndogs zone: defined\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: number typed value\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local ffi = require \"ffi\"\n\n            ffi.cdef[[\n                typedef struct {\n                    size_t  len;\n                    char   *data;\n                } ngx_str_t;\n\n                typedef struct {\n                    uint8_t         type;\n\n                    union {\n                        int         b; /* boolean */\n                        double      n; /* number */\n                        ngx_str_t   s; /* string */\n                    } value;\n\n                } ngx_http_lua_value_t;\n\n                void *ngx_http_lua_find_zone(char *data, size_t len);\n                intptr_t ngx_http_lua_shared_dict_get(void *zone, char *kdata, size_t klen, ngx_http_lua_value_t *val);\n            ]]\n\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 1234567)\n            dogs:set(\"bar\", 3.14159)\n\n            local buf = ffi.new(\"char[?]\", 4)\n\n            ffi.copy(buf, \"dogs\", 4)\n            local zone = ffi.C.ngx_http_lua_find_zone(buf, 4)\n\n            local val = ffi.new(\"ngx_http_lua_value_t[?]\", 1)\n\n            ffi.copy(buf, \"foo\", 3)\n            local rc = ffi.C.ngx_http_lua_shared_dict_get(zone, buf, 3, val)\n            ngx.say(\"foo: rc=\", tonumber(rc),\n                \", type=\", val[0].type,\n                \", val=\", tonumber(val[0].value.n))\n\n            ffi.copy(buf, \"bar\", 3)\n            local rc = ffi.C.ngx_http_lua_shared_dict_get(zone, buf, 3, val)\n            ngx.say(\"bar: rc=\", tonumber(rc),\n                \", type=\", val[0].type,\n                \", val=\", tonumber(val[0].value.n))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nfoo: rc=0, type=3, val=1234567\nbar: rc=0, type=3, val=3.14159\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: boolean typed value\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local ffi = require \"ffi\"\n\n            ffi.cdef[[\n                typedef struct {\n                    size_t  len;\n                    char   *data;\n                } ngx_str_t;\n\n                typedef struct {\n                    uint8_t         type;\n\n                    union {\n                        int         b; /* boolean */\n                        double      n; /* number */\n                        ngx_str_t   s; /* string */\n                    } value;\n\n                } ngx_http_lua_value_t;\n\n                void *ngx_http_lua_find_zone(char *data, size_t len);\n                intptr_t ngx_http_lua_shared_dict_get(void *zone, char *kdata, size_t klen, ngx_http_lua_value_t *val);\n            ]]\n\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", true)\n            dogs:set(\"bar\", false)\n\n            local buf = ffi.new(\"char[?]\", 4)\n\n            ffi.copy(buf, \"dogs\", 4)\n            local zone = ffi.C.ngx_http_lua_find_zone(buf, 4)\n\n            local val = ffi.new(\"ngx_http_lua_value_t[?]\", 1)\n\n            ffi.copy(buf, \"foo\", 3)\n            local rc = ffi.C.ngx_http_lua_shared_dict_get(zone, buf, 3, val)\n            ngx.say(\"foo: rc=\", tonumber(rc),\n                \", type=\", tonumber(val[0].type),\n                \", val=\", tonumber(val[0].value.b))\n\n            local val = ffi.new(\"ngx_http_lua_value_t[?]\", 1)\n            ffi.copy(buf, \"bar\", 3)\n            local rc = ffi.C.ngx_http_lua_shared_dict_get(zone, buf, 3, val)\n            ngx.say(\"bar: rc=\", tonumber(rc),\n                \", type=\", tonumber(val[0].type),\n                \", val=\", tonumber(val[0].value.b))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nfoo: rc=0, type=1, val=1\nbar: rc=0, type=1, val=0\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: key not found\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local ffi = require \"ffi\"\n\n            ffi.cdef[[\n                typedef struct {\n                    size_t  len;\n                    char   *data;\n                } ngx_str_t;\n\n                typedef struct {\n                    uint8_t         type;\n\n                    union {\n                        int         b; /* boolean */\n                        double      n; /* number */\n                        ngx_str_t   s; /* string */\n                    } value;\n\n                } ngx_http_lua_value_t;\n\n                void *ngx_http_lua_find_zone(char *data, size_t len);\n                intptr_t ngx_http_lua_shared_dict_get(void *zone, char *kdata, size_t klen, ngx_http_lua_value_t *val);\n            ]]\n\n            local dogs = ngx.shared.dogs\n            dogs:flush_all()\n\n            local buf = ffi.new(\"char[?]\", 4)\n\n            ffi.copy(buf, \"dogs\", 4)\n            local zone = ffi.C.ngx_http_lua_find_zone(buf, 4)\n\n            local val = ffi.new(\"ngx_http_lua_value_t[?]\", 1)\n\n            ffi.copy(buf, \"foo\", 3)\n            local rc = ffi.C.ngx_http_lua_shared_dict_get(zone, buf, 3, val)\n            ngx.say(\"foo: rc=\", tonumber(rc))\n\n            local val = ffi.new(\"ngx_http_lua_value_t[?]\", 1)\n            ffi.copy(buf, \"bar\", 3)\n            local rc = ffi.C.ngx_http_lua_shared_dict_get(zone, buf, 3, val)\n            ngx.say(\"bar: rc=\", tonumber(rc))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nfoo: rc=-5\nbar: rc=-5\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: string typed value\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local ffi = require \"ffi\"\n\n            ffi.cdef[[\n                typedef struct {\n                    size_t  len;\n                    char   *data;\n                } ngx_str_t;\n\n                typedef struct {\n                    uint8_t         type;\n\n                    union {\n                        int         b; /* boolean */\n                        double      n; /* number */\n                        ngx_str_t   s; /* string */\n                    } value;\n\n                } ngx_http_lua_value_t;\n\n                void *ngx_http_lua_find_zone(char *data, size_t len);\n                intptr_t ngx_http_lua_shared_dict_get(void *zone, char *kdata, size_t klen, ngx_http_lua_value_t *val);\n            ]]\n\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", \"hello world\")\n            dogs:set(\"bar\", \"\")\n\n            local buf = ffi.new(\"char[?]\", 4)\n\n            ffi.copy(buf, \"dogs\", 4)\n            local zone = ffi.C.ngx_http_lua_find_zone(buf, 4)\n\n            local s = ffi.new(\"char[?]\", 20)\n\n            local val = ffi.new(\"ngx_http_lua_value_t[?]\", 1)\n            val[0].value.s.len = 20\n            val[0].value.s.data = s\n\n            ffi.copy(buf, \"foo\", 3)\n            local rc = ffi.C.ngx_http_lua_shared_dict_get(zone, buf, 3, val)\n            ngx.say(\"foo: rc=\", tonumber(rc),\n                \", type=\", tonumber(val[0].type),\n                \", val=\", ffi.string(val[0].value.s.data, val[0].value.s.len),\n                \", len=\", tonumber(val[0].value.s.len))\n\n            local val = ffi.new(\"ngx_http_lua_value_t[?]\", 1)\n            val[0].value.s.len = 20\n            val[0].value.s.data = s\n\n            ffi.copy(buf, \"bar\", 3)\n            local rc = ffi.C.ngx_http_lua_shared_dict_get(zone, buf, 3, val)\n            ngx.say(\"bar: rc=\", tonumber(rc),\n                \", type=\", tonumber(val[0].type),\n                \", val=\", ffi.string(val[0].value.s.data, val[0].value.s.len),\n                \", len=\", tonumber(val[0].value.s.len))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nfoo: rc=0, type=4, val=hello world, len=11\nbar: rc=0, type=4, val=, len=0\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: nil typed value\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local ffi = require \"ffi\"\n\n            ffi.cdef[[\n                typedef struct {\n                    size_t  len;\n                    char   *data;\n                } ngx_str_t;\n\n                typedef struct {\n                    uint8_t         type;\n\n                    union {\n                        int         b; /* boolean */\n                        double      n; /* number */\n                        ngx_str_t   s; /* string */\n                    } value;\n\n                } ngx_http_lua_value_t;\n\n                void *ngx_http_lua_find_zone(char *data, size_t len);\n                intptr_t ngx_http_lua_shared_dict_get(void *zone, char *kdata, size_t klen, ngx_http_lua_value_t *val);\n            ]]\n\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", nil)\n\n            local buf = ffi.new(\"char[?]\", 4)\n\n            ffi.copy(buf, \"dogs\", 4)\n            local zone = ffi.C.ngx_http_lua_find_zone(buf, 4)\n\n            local val = ffi.new(\"ngx_http_lua_value_t[?]\", 1)\n\n            ffi.copy(buf, \"foo\", 3)\n            local rc = ffi.C.ngx_http_lua_shared_dict_get(zone, buf, 3, val)\n            ngx.say(\"foo: rc=\", tonumber(rc))\n        ';\n    }\n--- request\nGET /test\n--- response_body\nfoo: rc=-5\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: find zone (multiple zones)\n--- http_config\n    lua_shared_dict dogs 1m;\n    lua_shared_dict cats 1m;\n--- config\n    location = /test {\n        content_by_lua '\n            local ffi = require \"ffi\"\n\n            ffi.cdef[[\n                void *ngx_http_lua_find_zone(char *data, size_t len);\n            ]]\n\n            local buf = ffi.new(\"char[?]\", 4)\n            ffi.copy(buf, \"cats\", 4)\n            local zone = ffi.C.ngx_http_lua_find_zone(buf, 4)\n            local cats = tostring(zone)\n\n            ffi.copy(buf, \"dogs\", 4)\n            zone = ffi.C.ngx_http_lua_find_zone(buf, 4)\n            local dogs = tostring(zone)\n\n            ngx.say(\"dogs == cats ? \", dogs == cats)\n            -- ngx.say(\"dogs: \", dogs)\n            -- ngx.say(\"cats \", cats)\n        ';\n    }\n--- request\nGET /test\n--- response_body\ndogs == cats ? false\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/100-client-abort.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nBEGIN {\n    if ($ENV{TEST_NGINX_USE_HTTP3}) {\n        $SkipReason = \"client abort detect does not support in http3\";\n    } elsif ($ENV{TEST_NGINX_USE_HTTP2}) {\n        $SkipReason = \"client abort detect does not support in http2\";\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\nuse t::StapThread;\n\nour $GCScript = <<_EOC_;\n$t::StapThread::GCScript\n\nF(ngx_http_lua_check_broken_connection) {\n    println(\"lua check broken conn\")\n}\n\nF(ngx_http_lua_request_cleanup) {\n    println(\"lua req cleanup\")\n}\n_EOC_\n\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 1);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.sleep(1)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 2: sleep + stop (log handler still gets called)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.sleep(1)\n        ';\n        log_by_lua '\n            ngx.log(ngx.NOTICE, \"here in log by lua\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\nhere in log by lua\n\n\n\n=== TEST 3: sleep + ignore\n--- config\n    location /t {\n        lua_check_client_abort off;\n        content_by_lua '\n            ngx.sleep(1)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nlua req cleanup\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: subrequest + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.location.capture(\"/sub\")\n            error(\"bad things happen\")\n        ';\n    }\n\n    location /sub {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 5: subrequest + ignore\n--- config\n    location /t {\n        lua_check_client_abort off;\n        content_by_lua '\n            ngx.location.capture(\"/sub\")\n            error(\"bad things happen\")\n        ';\n    }\n\n    location /sub {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: fail\nlua req cleanup\ndelete thread 1\n\n--- wait: 1.1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- error_log\nbad things happen\n\n\n\n=== TEST 6: subrequest + stop (proxy, ignore client abort)\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.location.capture(\"/sub\")\n            error(\"bad things happen\")\n        ';\n    }\n\n    location = /sub {\n        proxy_ignore_client_abort on;\n        proxy_pass http://127.0.0.2:12345/;\n    }\n\n    location = /sleep {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.sleep(1)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 7: subrequest + stop (proxy, check client abort)\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.location.capture(\"/sub\")\n            error(\"bad things happen\")\n        ';\n    }\n\n    location = /sub {\n        proxy_ignore_client_abort off;\n        proxy_pass http://127.0.0.2:12345/;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 8: need body on + sleep + stop (log handler still gets called)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        lua_need_request_body on;\n        content_by_lua '\n            ngx.sleep(1)\n        ';\n        log_by_lua '\n            ngx.log(ngx.NOTICE, \"here in log by lua\")\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\nhere in log by lua\n\n\n\n=== TEST 9: ngx.req.read_body + sleep + stop (log handler still gets called)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.sleep(1)\n        ';\n        log_by_lua '\n            ngx.log(ngx.NOTICE, \"here in log by lua\")\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 0.1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\nhere in log by lua\n\n\n\n=== TEST 10: ngx.req.socket + receive() + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local sock = ngx.req.socket()\n            sock:receive()\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 11: ngx.req.socket + receive(N) + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local sock = ngx.req.socket()\n            sock:receive(5)\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 0.1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 12: ngx.req.socket + receive(n) + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local sock = ngx.req.socket()\n            sock:receive(2)\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like\n^(?:lua check broken conn\nterminate 1: ok\ndelete thread 1\nlua req cleanup|lua check broken conn\nlua req cleanup\ndelete thread 1)$\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: ngx.req.socket + m * receive(n) + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local sock = ngx.req.socket()\n            sock:receive(2)\n            sock:receive(2)\n            sock:receive(1)\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 14: ngx.req.socket + receiveuntil + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local sock = ngx.req.socket()\n            local it = sock:receiveuntil(\"\\\\n\")\n            it()\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 15: ngx.req.socket + receiveuntil + it(n) + sleep + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local sock = ngx.req.socket()\n            local it = sock:receiveuntil(\"\\\\n\")\n            it(2)\n            it(3)\n            ngx.sleep(1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- timeout: 0.2\n--- wait: 0.1\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 16: cosocket + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.req.discard_body()\n\n            local sock, err = ngx.socket.tcp()\n            if not sock then\n                ngx.log(ngx.ERR, \"failed to get socket: \", err)\n                return\n            end\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to connect: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"blpop nonexist 2\\\\r\\\\n\")\n            if not bytes then\n                ngx.log(ngx.ERR, \"failed to send query: \", err)\n                return\n            end\n\n            -- ngx.log(ngx.ERR, \"about to receive\")\n\n            local res, err = sock:receive()\n            if not res then\n                ngx.log(ngx.ERR, \"failed to receive query: \", err)\n                return\n            end\n\n            ngx.log(ngx.ERR, \"res: \", res)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 17: ngx.req.socket + receive n < content-length + ignore\n--- config\n    location /t {\n        lua_check_client_abort off;\n        content_by_lua '\n            local sock = ngx.req.socket()\n            local res, err, part = sock:receive(\"*a\")\n            if not res then\n                ngx.log(ngx.NOTICE, \"failed to receive: \", err, \": \", part)\n                return\n            end\n            error(\"bad\")\n        ';\n    }\n--- raw_request eval\n\"POST /t HTTP/1.0\\r\nHost: localhost\\r\nConnection: close\\r\nContent-Length: 100\\r\n\\r\nhello\"\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nfailed to receive: client aborted: hello\n\n\n\n=== TEST 18: ngx.req.socket + receive n < content-length + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local sock = ngx.req.socket()\n            local res, err, part = sock:receive(\"*a\")\n            if not res then\n                ngx.log(ngx.NOTICE, \"failed to receive: \", err, \": \", part)\n                return\n            end\n            error(\"bad\")\n        ';\n    }\n--- raw_request eval\n\"POST /t HTTP/1.0\\r\nHost: localhost\\r\nConnection: close\\r\nContent-Length: 100\\r\n\\r\nhello\"\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nfailed to receive: client aborted: hello\n\n\n\n=== TEST 19: ngx.req.socket + receive n == content-length + stop\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local sock = ngx.req.socket()\n            local res, err = sock:receive(\"*a\")\n            if not res then\n                ngx.log(ngx.NOTICE, \"failed to receive: \", err)\n                return\n            end\n            ngx.sleep(0.1)\n            error(\"bad\")\n        ';\n    }\n--- raw_request eval\n\"POST /t HTTP/1.0\\r\nHost: localhost\\r\nConnection: close\\r\nContent-Length: 5\\r\n\\r\nhello\"\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like\n^(?:lua check broken conn\nterminate 1: ok\ndelete thread 1\nlua req cleanup|lua check broken conn\nlua check broken conn\nlua req cleanup\ndelete thread 1)$\n\n--- shutdown\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n\n\n\n=== TEST 20: ngx.req.socket + receive n == content-length + ignore\n--- config\n    location /t {\n        content_by_lua '\n            local sock = ngx.req.socket()\n            local res, err = sock:receive(\"*a\")\n            if not res then\n                ngx.log(ngx.NOTICE, \"failed to receive: \", err)\n                return\n            end\n            ngx.say(\"done\")\n        ';\n    }\n--- raw_request eval\n\"POST /t HTTP/1.0\\r\nHost: localhost\\r\nConnection: close\\r\nContent-Length: 5\\r\n\\r\nhello\"\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nlua req cleanup\n\n--- shutdown: 1\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 21: ngx.req.read_body + sleep + stop (log handler still gets called)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.sleep(0.1)\n        ';\n    }\n--- request\nPOST /t\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- shutdown: 1\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\n--- SKIP\n\n\n\n=== TEST 22: exec to lua + ignore\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.exec(\"/t2\")\n        ';\n    }\n\n    location = /t2 {\n        lua_check_client_abort off;\n        content_by_lua '\n            ngx.sleep(1)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\nlua req cleanup\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nlua req cleanup\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: exec to proxy + ignore\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.exec(\"/t2\")\n        ';\n    }\n\n    location = /t2 {\n        proxy_ignore_client_abort on;\n        proxy_pass http://127.0.0.1:$server_port/sleep;\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 24: exec (named location) to proxy + ignore\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.exec(\"@t2\")\n        ';\n    }\n\n    location @t2 {\n        proxy_ignore_client_abort on;\n        proxy_pass http://127.0.0.1:$server_port/sleep;\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 25: bug in ngx_http_upstream_test_connect for kqueue\n--- config\n    location /t {\n        proxy_pass http://127.0.0.1:1234/;\n    }\n--- request\nGET /t\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- error_log eval\nqr{connect\\(\\) failed \\(\\d+: Connection refused\\) while connecting to upstream}\n--- no_error_log\n[alert]\n\n\n\n=== TEST 26: sleep (default off)\n--- config\n    location /t {\n        content_by_lua '\n            ngx.sleep(1)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nlua req cleanup\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 27: ngx.say\n--- config\n    location /t {\n        postpone_output 1;\n        content_by_lua '\n            ngx.sleep(0.2)\n            local ok, err = ngx.say(\"hello\")\n            if not ok then\n                ngx.log(ngx.WARN, \"say failed: \", err)\n                return\n            end\n        ';\n    }\n--- request\nGET /t\n\n--- wait: 0.2\n--- timeout: 0.1\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n--- error_log\nsay failed: nginx output filter error\n--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n\n\n\n=== TEST 28: ngx.print\n--- config\n    location /t {\n        postpone_output 1;\n        content_by_lua '\n            ngx.sleep(0.2)\n            local ok, err = ngx.print(\"hello\")\n            if not ok then\n                ngx.log(ngx.WARN, \"print failed: \", err)\n                return\n            end\n        ';\n    }\n--- request\nGET /t\n\n--- wait: 0.2\n--- timeout: 0.1\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n--- error_log\nprint failed: nginx output filter error\n--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n\n\n\n=== TEST 29: ngx.send_headers\n--- config\n    location /t {\n        postpone_output 1;\n        content_by_lua '\n            ngx.sleep(0.2)\n            local ok, err = ngx.send_headers()\n            if not ok then\n                ngx.log(ngx.WARN, \"send headers failed: \", err)\n                return\n            end\n            ngx.log(ngx.WARN, \"send headers succeeded\")\n        ';\n    }\n--- request\nGET /t\n\n--- wait: 0.2\n--- timeout: 0.1\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n--- error_log\nsend headers succeeded\n\n\n\n=== TEST 30: ngx.flush\n--- config\n    location /t {\n        #postpone_output 1;\n        content_by_lua '\n            ngx.say(\"hello\")\n            ngx.sleep(0.2)\n            local ok, err = ngx.flush()\n            if not ok then\n                ngx.log(ngx.WARN, \"flush failed: \", err)\n                return\n            end\n            ngx.log(ngx.WARN, \"flush succeeded\")\n        ';\n    }\n--- request\nGET /t\n\n--- wait: 0.2\n--- timeout: 0.1\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n--- error_log\nflush succeeded\n--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n\n\n\n=== TEST 31: ngx.eof\n--- config\n    location /t {\n        postpone_output 1;\n        content_by_lua '\n            ngx.sleep(0.2)\n            local ok, err = ngx.eof()\n            if not ok then\n                ngx.log(ngx.WARN, \"eof failed: \", err)\n                return\n            end\n            ngx.log(ngx.WARN, \"eof succeeded\")\n        ';\n    }\n--- request\nGET /t\n\n--- wait: 0.2\n--- timeout: 0.1\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\neof succeeded\n--- error_log\neof failed: nginx output filter error\n--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)\n"
  },
  {
    "path": "t/101-on-abort.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nBEGIN {\n    if ($ENV{TEST_NGINX_USE_HTTP3}) {\n        $SkipReason = \"client abort detect does not support in http3\";\n    } elsif ($ENV{TEST_NGINX_USE_HTTP2}) {\n        $SkipReason = \"client abort detect does not support in http2\";\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\nuse t::StapThread;\n\nour $GCScript = <<_EOC_;\n$t::StapThread::GCScript\n\nF(ngx_http_lua_check_broken_connection) {\n    println(\"lua check broken conn\")\n}\n\nF(ngx_http_lua_request_cleanup) {\n    println(\"lua req cleanup\")\n}\n_EOC_\n\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4 + 19);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: ignore the client abort event in the user callback\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out_like chop\n^create 2 in 1\nlua check broken conn\nterminate 2: ok\n(?:lua check broken conn\n)?terminate 1: ok\ndelete thread 2\ndelete thread 1\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- wait: 0.7\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 2: abort in the user callback\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(444)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- wait: 0.1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\nmain handler done\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 3: ngx.exit(499) with pending subrequest\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(499)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 4: ngx.exit(408) with pending subrequest\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(408)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- wait: 0.1\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 5: ngx.exit(-1) with pending subrequest\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(-1)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 1;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 6: ngx.exit(0) with pending subrequest\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(0)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.location.capture(\"/sleep\")\n            ngx.log(ngx.ERR, \"main handler done\")\n        ';\n    }\n\n    location = /sleep {\n        echo_sleep 0.7;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: fail\nterminate 1: ok\ndelete thread 2\ndelete thread 1\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- wait: 0.7\n--- ignore_response\n--- error_log eval\n[\n'client prematurely closed connection',\n'on abort called',\nqr/lua user thread aborted: runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):4: attempt to abort with pending subrequests/,\n'main handler done',\n]\n\n\n\n=== TEST 7: accessing cosocket in callback\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                local sock = ngx.socket.tcp()\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_REDIS_PORT)\n                if not ok then\n                    ngx.log(ngx.ERR, \"failed to connect to redis: \", err)\n                    ngx.exit(499)\n                end\n                local bytes, err = sock:send(\"flushall\\\\r\\\\n\")\n                if not bytes then\n                    ngx.log(ngx.ERR, \"failed to send query: \", err)\n                    ngx.exit(499)\n                end\n\n                local res, err = sock:receive()\n                if not res then\n                    ngx.log(ngx.ERR, \"failed to receive: \", err)\n                    ngx.exit(499)\n                end\n                ngx.log(ngx.NOTICE, \"callback done: \", res)\n                ngx.exit(499)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- wait: 0.5\n--- ignore_response\n--- no_error_log\n[error]\nmain handler done\n--- error_log\nclient prematurely closed connection\non abort called\ncallback done: +OK\n\n\n\n=== TEST 8: ignore the client abort event in the user callback (no check)\n--- config\n    location /t {\n        lua_check_client_abort off;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                ngx.say(\"cannot set on_abort: \", err)\n                return\n            end\n\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nterminate 1: ok\ndelete thread 1\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- response_body\ncannot set on_abort: lua_check_client_abort is off\n--- no_error_log\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 9: register on_abort callback but no client abortion\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.say(\"done\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nlua req cleanup\ndelete thread 2\n\n--- response_body\ndone\n--- no_error_log\n[error]\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 10: ignore the client abort event in the user callback (uthread)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.7)\n                ngx.log(ngx.NOTICE, \"main handler done\")\n            end)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nlua check broken conn\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\nlua req cleanup\n\n--- timeout: 0.2\n--- abort\n--- wait: 0.7\n--- ignore_response\n--- no_error_log\n[error]\n--- error_log\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 11: abort in the user callback (uthread)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(444)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.7)\n                ngx.log(ngx.NOTICE, \"main handler done\")\n            end)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 3\n\n--- timeout: 0.2\n--- wait: 0.1\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\nmain handler done\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 12: register on_abort callback but no client abortion (uthread)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.1)\n                ngx.say(\"done\")\n            end)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 3: ok\ndelete thread 3\nlua req cleanup\ndelete thread 2\n\n--- response_body\ndone\n--- no_error_log\n[error]\nclient prematurely closed connection\non abort called\nmain handler done\n\n\n\n=== TEST 13: register on_abort callback multiple times\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                ngx.say(\"1: cannot set on_abort: \" .. err)\n                return\n            end\n\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                ngx.say(\"2: cannot set on_abort: \" .. err)\n                return\n            end\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.1)\n                ngx.say(\"done\")\n            end)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nlua req cleanup\ndelete thread 2\n\n--- response_body\n2: cannot set on_abort: duplicate call\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: abort with 499 in the user callback, but the header is already sent\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(499)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.send_headers()\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\nmain handler done\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 15: abort with 444 in the user callback, but the header is already sent\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(444)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.send_headers()\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\nmain handler done\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 16: abort with 408 in the user callback, but the header is already sent\n--- config\n    location /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n                ngx.exit(408)\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.send_headers()\n            ngx.sleep(0.7)\n            ngx.log(ngx.NOTICE, \"main handler done\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nlua check broken conn\nterminate 2: ok\nlua req cleanup\ndelete thread 2\ndelete thread 1\n\n--- timeout: 0.2\n--- wait: 0.1\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\nmain handler done\n--- error_log\nclient prematurely closed connection\non abort called\n\n\n\n=== TEST 17: GC issue with the on_abort thread object\n--- config\n    location = /t {\n        lua_check_client_abort on;\n        content_by_lua '\n            ngx.on_abort(function () end)\n            collectgarbage()\n            ngx.sleep(60)\n        ';\n    }\n--- request\n    GET /t\n--- abort\n--- timeout: 0.2\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 18: register on_abort callback but no client abortion (2 uthreads and 1 pending)\n--- config\n    location /t {\n        lua_check_client_abort on;\n        rewrite_by_lua '\n            local ok, err = ngx.on_abort(function ()\n                ngx.log(ngx.NOTICE, \"on abort called\")\n            end)\n\n            if not ok then\n                error(\"cannot set on_abort: \" .. err)\n            end\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(0.1)\n                ngx.say(\"done\")\n                ngx.exit(200)\n            end)\n\n            ngx.thread.spawn(function ()\n                ngx.sleep(100)\n            end)\n        ';\n        content_by_lua return;\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nspawn user thread 3 in 1\ncreate 4 in 1\nspawn user thread 4 in 1\nterminate 1: ok\ndelete thread 1\nterminate 3: ok\nlua req cleanup\ndelete thread 2\ndelete thread 3\ndelete thread 4\n\n--- wait: 0.5\n--- response_body\ndone\n--- no_error_log\n[error]\nclient prematurely closed connection\non abort called\nmain handler done\n"
  },
  {
    "path": "t/102-req-start-time.t",
    "content": "# -*- mode: conf -*-\n# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\nlog_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: start time\n--- config\n    location = /start {\n        content_by_lua 'ngx.say(ngx.req.start_time())';\n    }\n--- request\nGET /start\n--- response_body_like: ^\\d{10,}(\\.\\d+)?$\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: start time in set_by_lua\n--- config\n    location = /start {\n        set_by_lua $a 'return ngx.req.start_time()';\n        echo $a;\n    }\n--- request\nGET /start\n--- response_body_like: ^\\d{10,}(\\.\\d+)?$\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: request time\n--- config\n    location = /req_time {\n        content_by_lua '\n            ngx.sleep(0.1)\n\n            local req_time = ngx.now() - ngx.req.start_time()\n\n            ngx.say(req_time)\n            ngx.say(ngx.req.start_time() < ngx.now())\n        ';\n    }\n--- request\nGET /req_time\n--- response_body_like chop\n^(?:0\\.[12]|0\\.099|0\\.098)\\d*\ntrue$\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: request time update\n--- config\n    location = /req_time {\n            content_by_lua '\n               ngx.sleep(0.1)\n\n               local req_time = ngx.now() - ngx.req.start_time()\n\n               ngx.sleep(0.1)\n\n               ngx.update_time()\n\n               local req_time_updated = ngx.now() - ngx.req.start_time()\n\n               ngx.say(req_time)\n               ngx.say(req_time_updated)\n               ngx.say(req_time_updated > req_time)\n            ';\n    }\n--- request\nGET /req_time\n--- response_body_like chomp\n^(?:0\\.[12]|0\\.099|0\\.098)\\d*\n0\\.\\d+\ntrue$\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: init_by_lua\n--- http_config\n    init_by_lua '\n        time = ngx.req.start_time()\n    ';\n--- config\n    location = /t {\n        content_by_lua '\n            ngx.say(time)\n        ';\n    }\n--- request\n    GET /t\n--- response_body\n--- no_error_log\n[error]\n--- SKIP\n"
  },
  {
    "path": "t/103-req-http-ver.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: HTTP 1.1\n--- config\n    location /t {\n        content_by_lua '\n            ngx.say(ngx.req.http_version())\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\nmy $body;\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    $body=\"3\\n\";\n} elsif (defined $ENV{TEST_NGINX_USE_HTTP2}) {\n    $body=\"2\\n\";\n} else {\n    $body=\"1.1\\n\";\n}\n\n$body;\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: HTTP 1.0\n--- config\n    location /t {\n        content_by_lua '\n            ngx.say(ngx.req.http_version())\n        ';\n    }\n--- request\nGET /t HTTP/1.0\n--- response_body\n1\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/104-req-raw-header.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_USE_HTTP3}) {\n        $SkipReason = \"http3 does not support ngx.req.raw_header()\";\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 15);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: small header\n--- config\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\nqq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n\\r\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: large header\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 30 561;\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- request\nGET /t\n--- more_headers eval\nCORE::join \"\\n\", map { \"Header$_: value-$_\" } 1..512\n\n--- response_body eval\nqq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\r\\n\\r\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 3: large header (no request line)\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 30 561;\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header(true))\n        ';\n    }\n--- request\nGET /t\n--- more_headers eval\nCORE::join \"\\n\", map { \"Header$_: value-$_\" } 1..512\n\n--- response_body eval\nqq{Host: localhost\\r\nConnection: close\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\r\\n\\r\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 4: small header (no request line)\n--- config\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header(true))\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\nqq{Host: localhost\\r\nConnection: close\\r\n\\r\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: small header (no request line, with leading CRLF)\n--- config\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header(true))\n        ';\n    }\n--- raw_request eval\n\"\\r\\nGET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n\\r\n\"\n--- response_body eval\nqq{Host: localhost\\r\nConnection: close\\r\n\\r\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: small header, with leading CRLF\n--- config\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- raw_request eval\n\"\\r\\nGET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n\\r\n\"\n--- response_body eval\nqq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n\\r\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: large header, with leading CRLF\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 30 561;\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n\n--- raw_request eval\n\"\\r\\nGET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n\".\n(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\r\\n\\r\\n\"\n\n--- response_body eval\nqq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\r\\n\\r\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 8: large header, with leading CRLF, excluding request line\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 30 561;\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header(true))\n        ';\n    }\n\n--- raw_request eval\n\"\\r\\nGET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n\".\n(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\r\\n\\r\\n\"\n\n--- response_body eval\nqq{Host: localhost\\r\nConnection: close\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\r\\n\\r\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 9: large header, with lots of leading CRLF, excluding request line\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 30 561;\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header(true))\n        ';\n    }\n\n--- raw_request eval\n(\"\\r\\n\" x 534) . \"GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n\".\n(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\r\\n\\r\\n\"\n\n--- response_body eval\nqq{Host: localhost\\r\nConnection: close\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\r\\n\\r\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 10: small header, pipelined\n--- config\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- pipelined_requests eval\n[\"GET /t\", \"GET /th\"]\n\n--- more_headers\nFoo: bar\n\n--- response_body eval\n[qq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: keep-alive\\r\nFoo: bar\\r\n\\r\n}, qq{GET /th HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\nFoo: bar\\r\n\\r\n}]\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: large header, pipelined\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 30 561;\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- pipelined_requests eval\n[\"GET /t\", \"GET /t\"]\n\n--- more_headers eval\nCORE::join \"\\n\", map { \"Header$_: value-$_\" } 1..512\n\n--- response_body eval\nmy $headers = (CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\r\\n\\r\\n\";\n\n[qq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: keep-alive\\r\n$headers},\nqq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n$headers}]\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 12: small header, multi-line header\n--- config\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\nFoo: bar baz\\r\n  blah\\r\n\\r\n\"\n--- response_body eval\nqq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\nFoo: bar baz\\r\n  blah\\r\n\\r\n}\n--- no_error_log\n[error]\n--- skip_nginx\n3: >= 1.21.1\n\n\n\n=== TEST 13: large header, multi-line header\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 50 567;\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n\n--- raw_request eval\nmy $headers = (CORE::join \"\\r\\n\", map { \"Header$_: value-$_\\r\\n hello $_ world blah blah\" } 1..512) . \"\\r\\n\\r\\n\";\n\nqq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n$headers}\n\n--- response_body eval\nqq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\\r\\n hello $_ world blah blah\" } 1..512) . \"\\r\\n\\r\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n--- skip_nginx\n3: >= 1.21.1\n\n\n\n=== TEST 14: small header (POST body)\n--- config\n    location /t {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- request\nPOST /t\nhello\n--- response_body eval\nqq{POST /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\nContent-Length: 5\\r\n\\r\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: small header (POST body) - in subrequests\n--- config\n    location /t {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/t\")\n            ngx.print(res.body)\n        ';\n    }\n\n--- request\nPOST /main\nhello\n--- response_body eval\nqq{POST /main HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\nContent-Length: 5\\r\n\\r\n}\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: large header (POST body)\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 30 561;\n    location /t {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- request\nPOST /t\nhello\n--- more_headers eval\nCORE::join\"\\n\", map { \"Header$_: value-$_\" } 1..512\n\n--- response_body eval\nqq{POST /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\r\\nContent-Length: 5\\r\\n\\r\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 17: large header (POST body) - in subrequests\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 30 561;\n    location /t {\n        content_by_lua '\n            ngx.req.read_body()\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n\n    location /main {\n        content_by_lua '\n            local res = ngx.location.capture(\"/t\")\n            ngx.print(res.body)\n        ';\n    }\n--- request\nPOST /main\nhello\n--- more_headers eval\nCORE::join\"\\n\", map { \"Header$_: value-$_\" } 1..512\n\n--- response_body eval\nqq{POST /main HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\r\\nContent-Length: 5\\r\\n\\r\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 18: large header (POST body) - r->header_end is outside r->header_in\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 30 564;\n    location /t {\n        content_by_lua '\n            -- ngx.req.read_body()\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- request\nPOST /t\nhello\n--- more_headers eval\nCORE::join(\"\\n\", map { \"Header$_: value-$_\" } 1..80) . \"\\nA: abcdefghijklmnopqrs\\n\"\n\n--- response_body eval\nqq{POST /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..80)\n. \"\\r\\nA: abcdefghijklmnopqrs\\r\\nContent-Length: 5\\r\\n\\r\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 19: large header (POST body) - r->header_end is outside r->header_in (2)\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 30 564;\n    location /t {\n        content_by_lua '\n            -- ngx.req.read_body()\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- request\nPOST /t\nhello\n--- more_headers eval\nCORE::join(\"\\n\", map { \"Header$_: value-$_\" } 1..52) . \"\\nA: abcdefghijklmnopqrs\\n\"\n\n--- response_body eval\nqq{POST /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..52)\n. \"\\r\\nA: abcdefghijklmnopqrs\\r\\nContent-Length: 5\\r\\n\\r\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 20: raw_header (the default header buffer can hold the request line, but not the header entries) - without request line)\n--- config\n    location /t {\n        content_by_lua '\n           ngx.print(ngx.req.raw_header(true))\n        ';\n    }\n--- request\nGET /t\n--- more_headers eval\nmy $s = \"User-Agent: curl\\nBah: bah\\n\";\n$s .= \"Accept: */*\\n\";\n$s .= \"Cookie: \" . \"C\" x 1200 . \"\\n\";\n$s\n--- response_body eval\n\"Host: localhost\\r\nConnection: close\\r\nUser-Agent: curl\\r\nBah: bah\\r\nAccept: */*\\r\nCookie: \" . (\"C\" x 1200) . \"\\r\\n\\r\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: raw_header (the default header buffer can hold the request line, but not the header entries) - with request line)\n--- config\n    location /t {\n        content_by_lua '\n           ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- request\nGET /t\n--- more_headers eval\nmy $s = \"User-Agent: curl\\nBah: bah\\n\";\n$s .= \"Accept: */*\\n\";\n$s .= \"Cookie: \" . \"C\" x 1200 . \"\\n\";\n$s\n--- response_body eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\nUser-Agent: curl\\r\nBah: bah\\r\nAccept: */*\\r\nCookie: \" . (\"C\" x 1200) . \"\\r\\n\\r\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: ngx_proxy/ngx_fastcgi/etc change r->header_end to point to their own buffers\n--- config\n    location = /t {\n        proxy_buffering off;\n        proxy_pass http://127.0.0.1:$server_port/bad;\n        proxy_intercept_errors on;\n        error_page 500 = /500;\n    }\n\n    location = /bad {\n        return 500;\n    }\n\n    location = /500 {\n        internal;\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n\\r\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: ngx_proxy/ngx_fastcgi/etc change r->header_end to point to their own buffers (exclusive LF in the request data)\n--- config\n    location = /t {\n        proxy_buffering off;\n        proxy_pass http://127.0.0.1:$server_port/bad;\n        proxy_intercept_errors on;\n        error_page 500 = /500;\n    }\n\n    location = /bad {\n        return 500;\n    }\n\n    location = /500 {\n        internal;\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- raw_request eval\n\"GET /t HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: 5\n\nhello\"\n--- response_body eval\n\"GET /t HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: 5\n\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 24: ngx_proxy/ngx_fastcgi/etc change r->header_end to point to their own buffers (exclusive LF in the request data, and no status line)\n--- config\n    location = /t {\n        proxy_buffering off;\n        proxy_pass http://127.0.0.1:$server_port/bad;\n        proxy_intercept_errors on;\n        error_page 500 = /500;\n    }\n\n    location = /bad {\n        return 500;\n    }\n\n    location = /500 {\n        internal;\n        content_by_lua '\n            ngx.print(ngx.req.raw_header(true))\n        ';\n    }\n--- raw_request eval\n\"GET /t HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: 5\n\nhello\"\n--- response_body eval\n\"Host: localhost\nConnection: close\nContent-Length: 5\n\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 25: ngx_proxy/ngx_fastcgi/etc change r->header_end to point to their own buffers (mixed LF and CRLF in the request data, and no status line)\n--- config\n    location = /t {\n        proxy_buffering off;\n        proxy_pass http://127.0.0.1:$server_port/bad;\n        proxy_intercept_errors on;\n        error_page 500 = /500;\n    }\n\n    location = /bad {\n        return 500;\n    }\n\n    location = /500 {\n        internal;\n        content_by_lua '\n            ngx.print(ngx.req.raw_header(true))\n        ';\n    }\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\nConnection: close\\r\nContent-Length: 5\\r\n\nhello\"\n--- response_body eval\n\"Host: localhost\nConnection: close\\r\nContent-Length: 5\\r\n\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 26: ngx_proxy/ngx_fastcgi/etc change r->header_end to point to their own buffers (another way of mixing LF and CRLF in the request data, and no status line)\n--- config\n    location = /t {\n        proxy_buffering off;\n        proxy_pass http://127.0.0.1:$server_port/bad;\n        proxy_intercept_errors on;\n        error_page 500 = /500;\n    }\n\n    location = /bad {\n        return 500;\n    }\n\n    location = /500 {\n        internal;\n        content_by_lua '\n            ngx.print(ngx.req.raw_header(true))\n        ';\n    }\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\nConnection: close\\r\nContent-Length: 5\n\\r\nhello\"\n--- response_body eval\n\"Host: localhost\nConnection: close\\r\nContent-Length: 5\n\\r\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 27: two pipelined requests with large headers\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 3 5610;\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- pipelined_requests eval\n[\"GET /t\", \"GET /t\"]\n--- more_headers eval\nCORE::join \"\\n\", map { \"Header$_: value-$_\" } 1..585\n\n--- response_body eval\n[qq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: keep-alive\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..585) . \"\\r\\n\\r\\n\",\nqq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..585) . \"\\r\\n\\r\\n\",\n,\n]\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 28: a request with large header and a smaller pipelined request following\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 2 1921;\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- pipelined_requests eval\n[\"GET /t\", \"GET /t\"]\n--- more_headers eval\n[CORE::join(\"\\n\", map { \"Header$_: value-$_\" } 1..170), \"Foo: bar\\n\"]\n\n--- response_body eval\n[qq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: keep-alive\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..170) . \"\\r\\n\\r\\n\",\nqq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\nFoo: bar\\r\n\\r\n},\n]\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 29: a request with large header and a smaller pipelined request following\n--- config\n    client_header_buffer_size 10;\n    large_client_header_buffers 2 1921;\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- pipelined_requests eval\n[\"GET /t\", \"GET /t\" . (\"a\" x 512)]\n--- more_headers eval\n[CORE::join(\"\\n\", map { \"Header$_: value-$_\" } 1..170), \"Foo: bar\\n\"]\n\n--- response_body eval\n[qq{GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: keep-alive\\r\n}\n.(CORE::join \"\\r\\n\", map { \"Header$_: value-$_\" } 1..170) . \"\\r\\n\\r\\n\",\nqq{GET /t} . (\"a\" x 512) . qq{ HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\nFoo: bar\\r\n\\r\n},\n]\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 30: large headers (using single LF as line break)\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.print(ngx.req.raw_header())\n        }\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.1\nHost: localhost\nConnection: close\n\".\n(CORE::join \"\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\n\\n\"\n\n--- response_body eval\nqq{GET /t HTTP/1.1\nHost: localhost\nConnection: close\n}\n.(CORE::join \"\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\n\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 31: large headers without request line (using single LF as line break)\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.print(ngx.req.raw_header(true))\n        }\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.1\nHost: localhost\nConnection: close\n\".\n(CORE::join \"\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\n\\n\"\n\n--- response_body eval\nqq{Host: localhost\nConnection: close\n}\n.(CORE::join \"\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\n\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 32: large headers with leading CRLF (using single LF as line break)\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.print(ngx.req.raw_header())\n        }\n    }\n\n--- raw_request eval\n\"\\r\nGET /t HTTP/1.1\nHost: localhost\nConnection: close\n\".\n(CORE::join \"\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\n\\n\"\n\n--- response_body eval\nqq{GET /t HTTP/1.1\nHost: localhost\nConnection: close\n}\n.(CORE::join \"\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\n\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 33: large headers without request line but contains leading CRLF (using single LF as line break)\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.print(ngx.req.raw_header(true))\n        }\n    }\n\n--- raw_request eval\n\"\\r\nGET /t HTTP/1.1\nHost: localhost\nConnection: close\n\".\n(CORE::join \"\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\n\\n\"\n\n--- response_body eval\nqq{Host: localhost\nConnection: close\n}\n.(CORE::join \"\\n\", map { \"Header$_: value-$_\" } 1..512) . \"\\n\\n\"\n\n--- no_error_log\n[error]\n--- timeout: 5\n\n\n\n=== TEST 34: multi-line header is invalid (nginx >= 1.21.1)\n--- config\n    location /t {\n        content_by_lua '\n            ngx.print(ngx.req.raw_header())\n        ';\n    }\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nConnection: close\\r\nFoo: bar baz\\r\n  blah\\r\n\\r\n\"\n--- error_code: 400\n--- error_log\nclient sent invalid header line: \"\\x20...\" while reading client request headers\n--- no_error_log\n[error]\n--- skip_nginx\n3: < 1.21.1\n\n\n\n=== TEST 35: bugfix: invalid http request\n--- log_level: error\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;;\";\n--- config\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_SERVER_PORT)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to connect to memc: \", err)\n                return\n            end\n            sock:send(\"\\n\")\n            sock:close()\n\n            ngx.say(\"OK\")\n        }\n    }\n\n    log_by_lua_block {\n        local h = ngx.req.raw_header()\n    }\n\n--- request\nGET /t\n--- response_body\nOK\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/105-pressure.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nworker_connections(1014);\n#master_on();\n#log_level('debug');\n\nrepeat_each(20);\n\nplan tests => repeat_each() * (blocks() * 3);\n\nour $HtmlDir = html_dir;\n#warn $html_dir;\n\nour $Id;\n\n#no_diff();\n#no_long_string();\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\n#no_shuffle();\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: memory issue in the \"args\" string option for ngx.location.capture\nthe default worker_connections is 64, HTTP3 will keep the connection when curl\nrequest finished. So need to change the worker_connection.\n--- config\n    location /test1 {\n        content_by_lua '\n            local res = ngx.location.capture(\"/test2/auth\", {args = ngx.var.args})\n            ngx.print(res.body)\n        ';\n    }\n    location /test2 {\n        content_by_lua '\n            collectgarbage()\n            ngx.say(ngx.var.args)\n        ';\n    }\n\n--- request eval\n$::Id = int rand 10000;\n\"GET /test1?parent=$::Id&name=2013031816214284300707&footprint=dsfasfwefklds\"\n\n--- response_body eval\n\"parent=$::Id&name=2013031816214284300707&footprint=dsfasfwefklds\\n\"\n\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/106-timer.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 8 + 61);\n\n#no_diff();\nno_long_string();\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir;\n\nworker_connections(1024);\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple at\n--- config\n    location /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function f(premature)\n                print(\"elapsed: \", ngx.now() - begin)\n                print(\"timer prematurely expired: \", premature)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\ntimer prematurely expired: true\n\n--- error_log eval\n[\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: 0\\.0(?:4[4-9]|5[0-6])\\d*, context: ngx\\.timer, client: \\d+\\.\\d+\\.\\d+\\.\\d+, server: 0\\.0\\.0\\.0:\\d+/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"timer prematurely expired: false\",\n]\n--- grep_error_log eval: qr/lua caching unused lua thread|lua reusing cached lua thread/\n--- grep_error_log_out eval\n[\n    \"lua caching unused lua thread\nlua caching unused lua thread\n\",\n    \"lua reusing cached lua thread\nlua reusing cached lua thread\nlua caching unused lua thread\nlua caching unused lua thread\n\",\n]\n\n\n\n=== TEST 2: globals are shared\n--- config\n    location /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function f()\n                foo = 3\n                print(\"elapsed: \", ngx.now() - begin)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.06)\n            ngx.say(\"foo = \", foo)\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\nfoo = 3\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: 0\\.0(?:4[4-9]|5[0-6])/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 3: lua variable sharing via upvalue\n--- config\n    location /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local foo\n            local function f()\n                foo = 3\n                print(\"elapsed: \", ngx.now() - begin)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.06)\n            ngx.say(\"foo = \", foo)\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\nfoo = 3\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: 0\\.0(?:4[4-9]|5[0-6])/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 4: simple at (sleep in the timer callback)\n--- config\n    location /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n                ngx.sleep(0.2)\n                print(\"elapsed: \", ngx.now() - begin)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.3\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[lua\\] .*? my lua timer handler/,\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: 0\\.(?:1[4-9]|2[0-6]?)/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 5: tcp cosocket in timer handler (short connections)\n--- no_http2\n--- config\n    server_tokens off;\n\n    location = /gc {\n        content_by_lua_block {\n            local c = collectgarbage(\"count\")\n            ngx.say(\"before: \", c)\n            collectgarbage(\"collect\")\n            c = collectgarbage(\"count\")\n            ngx.say(\"after: \", c)\n        }\n    }\n\n    location = /t {\n        content_by_lua '\n            collectgarbage()\n            -- ngx.say(\"gc size: \", collectgarbage(\"count\"))\n            local begin = ngx.now()\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n            local function f()\n                print(\"my lua timer handler\")\n                local sock = ngx.socket.tcp()\n                local port = $TEST_NGINX_SERVER_PORT\n                local ok, err = sock:connect(\"127.0.0.1\", port)\n                if not ok then\n                    fail(\"failed to connect: \", err)\n                    return\n                end\n\n                print(\"connected: \", ok)\n\n                local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                -- req = \"OK\"\n\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    fail(\"failed to send request: \", err)\n                    return\n                end\n\n                print(\"request sent: \", bytes)\n\n                while true do\n                    local line, err, part = sock:receive()\n                    if line then\n                        print(\"received: \", line)\n\n                    else\n                        if err == \"closed\" then\n                            break\n                        end\n                        fail(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                        break\n                    end\n                end\n\n                ok, err = sock:close()\n                print(\"close: \", ok, \" \", err)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            -- ngx.sleep(0.1)\n            ngx.say(\"registered timer\")\n        ';\n    }\n\n    location = /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"connected: 1\",\n\"request sent: 57\",\n\"received: HTTP/1.1 200 OK\",\nqr/received: Server: \\S+/,\n\"received: Content-Type: text/plain\",\n\"received: Content-Length: 4\",\n\"received: Connection: close\",\n\"received: foo\",\n\"close: 1 nil\",\n]\n\n\n\n=== TEST 6: tcp cosocket in timer handler (keep-alive connections)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n\n--- config\n    location = /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n\n                local test = require \"test\"\n                local port = $TEST_NGINX_MEMCACHED_PORT\n                test.go(port)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal function fail(...)\n    ngx.log(ngx.ERR, ...)\nend\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        fail(\"failed to connect: \", err)\n        return\n    end\n\n    print(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        fail(\"failed to send request: \", err)\n        return\n    end\n    print(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        print(\"received: \", line)\n\n    else\n        fail(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        fail(\"failed to set reusable: \", err)\n    end\nend\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\nqr/go\\(\\): connected: 1, reused: \\d+/,\n\"go(): request sent: 11\",\n\"go(): received: OK\",\n]\n\n\n\n=== TEST 7: 0 timer\n--- config\n    location /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"elapsed: \", ngx.now() - begin)\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: 0(?:[^.]|\\.00)/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 8: udp cosocket in timer handler\n--- config\n    location = /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n            local function f()\n                print(\"my lua timer handler\")\n                local socket = ngx.socket\n                -- local socket = require \"socket\"\n\n                local udp = socket.udp()\n\n                local port = $TEST_NGINX_MEMCACHED_PORT\n                udp:settimeout(1000) -- 1 sec\n\n                local ok, err = udp:setpeername(\"127.0.0.1\", port)\n                if not ok then\n                    fail(\"failed to connect: \", err)\n                    return\n                end\n\n                print(\"connected: \", ok)\n\n                local req = \"\\\\0\\\\1\\\\0\\\\0\\\\0\\\\1\\\\0\\\\0flush_all\\\\r\\\\n\"\n                local ok, err = udp:send(req)\n                if not ok then\n                    fail(\"failed to send: \", err)\n                    return\n                end\n\n                local data, err = udp:receive()\n                if not data then\n                    fail(\"failed to receive data: \", err)\n                    return\n                end\n                print(\"received \", #data, \" bytes: \", data)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n\n    location = /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"connected: 1\",\n\"received 12 bytes: \\x{00}\\x{01}\\x{00}\\x{00}\\x{00}\\x{01}\\x{00}\\x{00}OK\\x{0d}\\x{0a}\"\n]\n\n\n\n=== TEST 9: simple at (sleep in the timer callback) - log_by_lua\n--- config\n    location /t {\n        echo hello world;\n        log_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n                ngx.sleep(0.02)\n                print(\"elapsed: \", ngx.now() - begin)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set timer: \", err)\n                return\n            end\n            print(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nhello world\n\n--- wait: 0.3\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"registered timer\",\nqr/\\[lua\\] .*? my lua timer handler/,\nqr/\\[lua\\] log_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: 0\\.0(?:6[4-9]|7[0-9]|8[1-3])/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 10: tcp cosocket in timer handler (keep-alive connections) - log_by_lua\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n\n--- config\n    location = /t {\n        echo hello;\n        log_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n\n                local test = require \"test\"\n                local port = $TEST_NGINX_MEMCACHED_PORT\n                test.go(port)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set timer: \", err)\n                return\n            end\n            print(\"registered timer\")\n        ';\n    }\n\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal function fail(...)\n    ngx.log(ngx.ERR, ...)\nend\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        fail(\"failed to connect: \", err)\n        return\n    end\n\n    print(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        fail(\"failed to send request: \", err)\n        return\n    end\n    print(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        print(\"received: \", line)\n\n    else\n        fail(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        fail(\"failed to set reusable: \", err)\n    end\nend\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nhello\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"registered timer\",\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\nqr/go\\(\\): connected: 1, reused: \\d+/,\n\"go(): request sent: 11\",\n\"go(): received: OK\",\n]\n\n\n\n=== TEST 11: tcp cosocket in timer handler (keep-alive connections) - header_filter_by_lua\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n\n--- config\n    location = /t {\n        echo hello;\n        header_filter_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n\n                local test = require \"test\"\n                local port = $TEST_NGINX_MEMCACHED_PORT\n                test.go(port)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set timer: \", err)\n                return\n            end\n            print(\"registered timer\")\n        ';\n    }\n\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal function fail(...)\n    ngx.log(ngx.ERR, ...)\nend\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        fail(\"failed to connect: \", err)\n        return\n    end\n\n    print(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        fail(\"failed to send request: \", err)\n        return\n    end\n    print(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        print(\"received: \", line)\n\n    else\n        fail(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        fail(\"failed to set reusable: \", err)\n    end\nend\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap3\nglobal count = 0\nF(ngx_http_lua_header_filter) {\n    if (count++ == 10) {\n        println(\"header filter\")\n        print_ubacktrace()\n    }\n}\n\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nhello\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"registered timer\",\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\nqr/go\\(\\): connected: 1, reused: \\d+/,\n\"go(): request sent: 11\",\n\"go(): received: OK\",\n]\n\n\n\n=== TEST 12: tcp cosocket in timer handler (keep-alive connections) - body_filter_by_lua\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n\n--- config\n    location = /t {\n        echo hello;\n        body_filter_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n\n                local test = require \"test\"\n                local port = $TEST_NGINX_MEMCACHED_PORT\n                test.go(port)\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set timer: \", err)\n                return\n            end\n            print(\"registered timer\")\n        ';\n    }\n\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal function fail(...)\n    ngx.log(ngx.ERR, ...)\nend\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        fail(\"failed to connect: \", err)\n        return\n    end\n\n    print(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        fail(\"failed to send request: \", err)\n        return\n    end\n    print(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        print(\"received: \", line)\n\n    else\n        fail(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        fail(\"failed to set keep alive: \", err)\n    end\nend\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap3\nglobal count = 0\nF(ngx_http_lua_header_filter) {\n    if (count++ == 10) {\n        println(\"header filter\")\n        print_ubacktrace()\n    }\n}\n\n--- stap eval: $::GCScript\n--- stap_out_like chop\ncreate 2 in 1\ncreate 3 in 1\n(?:terminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n|terminate 3: ok\ndelete thread 3\nterminate 2: ok\ndelete thread 2)$\n\n--- response_body\nhello\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"registered timer\",\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\nqr/go\\(\\): connected: 1, reused: \\d+/,\n\"go(): request sent: 11\",\n\"go(): received: OK\",\n]\n\n\n\n=== TEST 13: tcp cosocket in timer handler (keep-alive connections) - set_by_lua\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n\n--- config\n    location = /t {\n        set_by_lua $a '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n\n                local test = require \"test\"\n                local port = $TEST_NGINX_MEMCACHED_PORT\n                test.go(port)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set timer: \", err)\n                return\n            end\n            print(\"registered timer\")\n            return 32\n        ';\n        echo $a;\n    }\n\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal function fail(...)\n    ngx.log(ngx.ERR, ...)\nend\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        fail(\"failed to connect: \", err)\n        return\n    end\n\n    print(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        fail(\"failed to send request: \", err)\n        return\n    end\n    print(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        print(\"received: \", line)\n\n    else\n        fail(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        fail(\"failed to set reusable: \", err)\n    end\nend\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap3\nglobal count = 0\nF(ngx_http_lua_header_filter) {\n    if (count++ == 10) {\n        println(\"header filter\")\n        print_ubacktrace()\n    }\n}\n\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\n32\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"registered timer\",\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\nqr/go\\(\\): connected: 1, reused: \\d+/,\n\"go(): request sent: 11\",\n\"go(): received: OK\",\n]\n\n\n\n=== TEST 14: coroutine API\n--- config\n    location /t {\n        content_by_lua '\n            local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n            local function f()\n                local function f()\n                    local cnt = 0\n                    for i = 1, 20 do\n                        print(\"cnt = \", cnt)\n                        cy()\n                        cnt = cnt + 1\n                    end\n                end\n\n                local c = cc(f)\n                for i=1,3 do\n                    cr(c)\n                    print(\"after resume, i = \", i)\n                end\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\ncreate 3 in 2\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"cnt = 0\",\n\"after resume, i = 1\",\n\"cnt = 1\",\n\"after resume, i = 2\",\n\"cnt = 2\",\n\"after resume, i = 3\",\n]\n\n\n\n=== TEST 15: ngx.thread API\n--- config\n    location /t {\n        content_by_lua '\n            local function fail (...)\n                ngx.log(ngx.ERR, ...)\n            end\n            local function handle()\n                local function f()\n                    print(\"hello in thread\")\n                    return \"done\"\n                end\n\n                local t, err = ngx.thread.spawn(f)\n                if not t then\n                    fail(\"failed to spawn thread: \", err)\n                    return\n                end\n\n                print(\"thread created: \", coroutine.status(t))\n\n                collectgarbage()\n\n                local ok, res = ngx.thread.wait(t)\n                if not ok then\n                    fail(\"failed to run thread: \", res)\n                    return\n                end\n\n                print(\"wait result: \", res)\n            end\n            local ok, err = ngx.timer.at(0.01, handle)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 3: ok\ndelete thread 3\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"hello in thread\",\n\"thread created: zombie\",\n\"wait result: done\",\n]\n\n\n\n=== TEST 16: shared dict\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                local dogs = ngx.shared.dogs\n                dogs:set(\"foo\", 32)\n                dogs:set(\"bah\", 10502)\n                local val = dogs:get(\"foo\")\n                print(\"get foo: \", val, \" \", type(val))\n                val = dogs:get(\"bah\")\n                print(\"get bah: \", val, \" \", type(val))\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"get foo: 32 number\",\n\"get bah: 10502 number\",\n]\n\n\n\n=== TEST 17: ngx.exit(0)\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                local function g()\n                    print(\"BEFORE ngx.exit\")\n                    ngx.exit(0)\n                end\n                g()\n                print(\"CANNOT REACH HERE\")\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"BEFORE ngx.exit\",\n]\n--- no_error_log\nCANNOT REACH HERE\nAPI disabled\n\n\n\n=== TEST 18: ngx.exit(403)\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                local function g()\n                    print(\"BEFORE ngx.exit\")\n                    ngx.exit(403)\n                end\n                g()\n                print(\"CANNOT REACH HERE\")\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\nCANNOT REACH HERE\nAPI disabled\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"BEFORE ngx.exit\",\n]\n\n\n\n=== TEST 19: exit in user thread (entry thread is still pending on ngx.sleep)\n--- quic_max_idle_timeout: 1.3\n--- config\n    location /t {\n        content_by_lua '\n            local function handle()\n                local function f()\n                    print(\"hello in thread\")\n                    ngx.sleep(0.1)\n                    ngx.exit(0)\n                end\n\n                print(\"BEFORE thread spawn\")\n                ngx.thread.spawn(f)\n                print(\"AFTER thread spawn\")\n                ngx.sleep(1)\n                print(\"entry thread END\")\n            end\n            local ok, err = ngx.timer.at(0.05, handle)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out_like chop\n(?:create 2 in 1\nterminate 1: ok\ndelete thread 1\nfree request\ncreate 3 in 2\nspawn user thread 3 in 2\nadd timer 100\nadd timer 1000\nexpire timer 100\nterminate 3: ok\ndelete thread 3\nlua sleep cleanup\ndelete timer 1000\ndelete thread 2|create 2 in 1\nterminate 1: ok\ndelete thread 1\ncreate 3 in 2\nspawn user thread 3 in 2\nadd timer 100\nadd timer 1000\nfree request\nexpire timer 100\nterminate 3: ok\ndelete thread 3\nlua sleep cleanup\ndelete timer 1000\ndelete thread 2)$\n\n--- response_body\nregistered timer\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\nAPI disabled\nentry thread END\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"BEFORE thread spawn\",\n\"hello in thread\",\n\"AFTER thread spawn\",\n]\n\n\n\n=== TEST 20: chained timers (0 delay)\n--- config\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local function g()\n                s = s .. \"[g]\"\n                print(\"trace: \", s)\n            end\n\n            local function f()\n                local ok, err = ngx.timer.at(0, g)\n                if not ok then\n                    fail(\"failed to set timer: \", err)\n                    return\n                end\n                s = s .. \"[f]\"\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\ncreate 3 in 2\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n'lua ngx.timer expired',\n'http lua close fake http connection',\nqr/trace: \\[m\\]\\[f\\]\\[g\\], context: ngx\\.timer, client: \\d+\\.\\d+\\.\\d+\\.\\d+, server: 0\\.0\\.0\\.0:\\d+/,\n]\n\n\n\n=== TEST 21: chained timers (non-zero delay)\n--- config\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local function g()\n                s = s .. \"[g]\"\n                print(\"trace: \", s)\n            end\n\n            local function f()\n                local ok, err = ngx.timer.at(0.01, g)\n                if not ok then\n                    fail(\"failed to set timer: \", err)\n                    return\n                end\n                s = s .. \"[f]\"\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\ncreate 3 in 2\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log\nlua ngx.timer expired\nhttp lua close fake http connection\ntrace: [m][f][g]\n\n\n\n=== TEST 22: multiple parallel timers\n--- config\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local function g()\n                s = s .. \"[g]\"\n                print(\"trace: \", s)\n            end\n\n            local function f()\n                s = s .. \"[f]\"\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                fail(\"failed to set timer: \", err)\n                return\n            end\n            local ok, err = ngx.timer.at(0.01, g)\n            if not ok then\n                fail(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log\nlua ngx.timer expired\nhttp lua close fake http connection\ntrace: [m][f][g]\n\n\n\n=== TEST 23: lua_max_pending_timers\n--- http_config\n    lua_max_pending_timers 1;\n--- config\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local function g()\n                s = s .. \"[g]\"\n                print(\"trace: \", s)\n            end\n\n            local function f()\n                s = s .. \"[f]\"\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer f: \", err)\n                return\n            end\n            local ok, err = ngx.timer.at(0.01, g)\n            if not ok then\n                ngx.say(\"failed to set timer g: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nfailed to set timer g: too many pending timers\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n[error]\n\n--- error_log\nlua ngx.timer expired\nhttp lua close fake http connection\n\n\n\n=== TEST 24: lua_max_pending_timers (just not exceeding)\n--- http_config\n    lua_max_pending_timers 2;\n--- config\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local function g()\n                s = s .. \"[g]\"\n                print(\"trace: \", s)\n            end\n\n            local function f()\n                s = s .. \"[f]\"\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer f: \", err)\n                return\n            end\n            local ok, err = ngx.timer.at(0.01, g)\n            if not ok then\n                ngx.say(\"failed to set timer g: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n[error]\n\n--- error_log\nlua ngx.timer expired\nhttp lua close fake http connection\ntrace: [m][f][g]\n\n\n\n=== TEST 25: lua_max_pending_timers - chained timers (non-zero delay) - not exceeding\n--- http_config\n    lua_max_pending_timers 1;\n\n--- config\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local function g()\n                s = s .. \"[g]\"\n                print(\"trace: \", s)\n            end\n\n            local function f()\n                local ok, err = ngx.timer.at(0.01, g)\n                if not ok then\n                    fail(\"failed to set timer: \", err)\n                    return\n                end\n                s = s .. \"[f]\"\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\ncreate 3 in 2\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log\nlua ngx.timer expired\nhttp lua close fake http connection\ntrace: [m][f][g]\n\n\n\n=== TEST 26: lua_max_pending_timers - chained timers (zero delay) - not exceeding\n--- http_config\n    lua_max_pending_timers 1;\n\n--- config\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local function g()\n                s = s .. \"[g]\"\n                print(\"trace: \", s)\n            end\n\n            local function f()\n                local ok, err = ngx.timer.at(0, g)\n                if not ok then\n                    fail(\"failed to set timer: \", err)\n                    return\n                end\n                s = s .. \"[f]\"\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\ncreate 3 in 2\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log\nlua ngx.timer expired\nhttp lua close fake http connection\ntrace: [m][f][g]\n\n\n\n=== TEST 27: lua_max_running_timers (just not enough)\n--- http_config\n    lua_max_running_timers 1;\n--- config\n    location /t {\n        content_by_lua '\n            collectgarbage()\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local f, g\n\n            g = function ()\n                ngx.sleep(0.01)\n            end\n\n            f = function ()\n                ngx.sleep(0.01)\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.say(\"failed to set timer f: \", err)\n                return\n            end\n            local ok, err = ngx.timer.at(0, g)\n            if not ok then\n                ngx.say(\"failed to set timer g: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[crit]\n[error]\n\n--- error_log eval\n[\nqr/\\[alert\\] .*? lua failed to run timer with function defined at =content_by_lua\\(nginx.conf:\\d+\\):11: 1 lua_max_running_timers are not enough/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n]\n\n\n\n=== TEST 28: lua_max_running_timers (just enough)\n--- http_config\n    lua_max_running_timers 2;\n--- config\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local f, g\n\n            g = function ()\n                ngx.sleep(0.01)\n            end\n\n            f = function ()\n                ngx.sleep(0.01)\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.say(\"failed to set timer f: \", err)\n                return\n            end\n            local ok, err = ngx.timer.at(0, g)\n            if not ok then\n                ngx.say(\"failed to set timer g: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n[error]\n\n--- error_log\nlua ngx.timer expired\nhttp lua close fake http connection\n\n\n\n=== TEST 29: lua_max_running_timers (just enough) - 2\n--- http_config\n    lua_max_running_timers 2;\n--- config\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local f, g\n\n            g = function ()\n                ngx.timer.at(0.02, f)\n                ngx.sleep(0.01)\n            end\n\n            f = function ()\n                ngx.sleep(0.01)\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.say(\"failed to set timer f: \", err)\n                return\n            end\n            local ok, err = ngx.timer.at(0, g)\n            if not ok then\n                ngx.say(\"failed to set timer g: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 1\nterminate 1: ok\ndelete thread 1\ncreate 4 in 3\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\nterminate 4: ok\ndelete thread 4\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n[error]\n\n--- error_log\nlua ngx.timer expired\nhttp lua close fake http connection\n\n\n\n=== TEST 30: user args\n--- config\n    location /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function f(premature, a, b, c)\n                print(\"elapsed: \", ngx.now() - begin)\n                print(\"timer prematurely expired: \", premature)\n                print(\"timer user args: \", a, \" \", b, \" \", c)\n            end\n            local ok, err = ngx.timer.at(0.05, f, 1, \"hello\", true)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\ntimer prematurely expired: true\n\n--- error_log eval\n[\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: 0\\.0(?:4[4-9]|5[0-6])\\d*, context: ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"timer prematurely expired: false\",\n\"timer user args: 1 hello true\",\n]\n\n\n\n=== TEST 31: use of ngx.ctx\n--- config\n    location /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function f(premature)\n                ngx.ctx.s = \"hello\"\n                print(\"elapsed: \", ngx.now() - begin)\n                print(\"timer prematurely expired: \", premature)\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\ntimer prematurely expired: true\n\n--- error_log eval\n[\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: .*?, context: ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"timer prematurely expired: false\",\n\"lua release ngx.ctx at ref \",\n]\n\n\n\n=== TEST 32: syslog error log\n--- http_config\n    #error_log syslog:server=127.0.0.1:12345 error;\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.log(ngx.ERR, \"Bad bad bad\")\n            end\n            ngx.timer.at(0, f)\n            ngx.sleep(0.001)\n            ngx.say(\"ok\")\n        ';\n    }\n--- log_level: error\n--- error_log_file: syslog:server=127.0.0.1:$TEST_NGINX_RAND_PORT_1\n--- udp_listen: $TEST_NGINX_RAND_PORT_1\n--- udp_query eval: qr/Bad bad bad/\n--- udp_reply: hello\n--- wait: 0.1\n--- request\n    GET /t\n--- response_body\nok\n--- error_log\nBad bad bad\n--- skip_nginx: 4: < 1.7.1\n\n\n\n=== TEST 33: log function location when failed to run a timer\n--- http_config\n    lua_max_running_timers 1;\n--- config\n    location /t {\n        content_by_lua_block {\n            local function g()\n                ngx.sleep(0.01)\n            end\n\n            local function f()\n                ngx.sleep(0.01)\n            end\n\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.say(\"failed to create timer f: \", err)\n                return\n            end\n\n            local ok, err = ngx.timer.at(0, g)\n            if not ok then\n                ngx.say(\"failed to create timer g: \", err)\n                return\n            end\n\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- wait: 0.1\n--- error_log eval\nqr/\\[alert\\] .*? lua failed to run timer with function defined at =content_by_lua\\(nginx.conf:\\d+\\):2: 1 lua_max_running_timers are not enough/\n--- no_error_log\n[crit]\n[error]\n\n\n\n=== TEST 34: log function location when failed to run a timer (anonymous function)\n--- http_config\n    lua_max_running_timers 1;\n--- config\n    location /t {\n        content_by_lua_block {\n            local function f()\n                ngx.sleep(0.01)\n            end\n\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.say(\"failed to set timer f: \", err)\n                return\n            end\n\n            local ok, err = ngx.timer.at(0, function()\n                ngx.sleep(0.01)\n            end)\n\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- wait: 0.1\n--- error_log eval\nqr/\\[alert\\] .*? lua failed to run timer with function defined at =content_by_lua\\(nginx.conf:\\d+\\):12: 1 lua_max_running_timers are not enough/\n--- no_error_log\n[crit]\n[error]\n\n\n\n=== TEST 35: log function location when failed to run a timer (lua file)\n--- user_files\n>>> test.lua\nlocal _M = {}\n\nfunction _M.run()\n    ngx.sleep(0.01)\nend\n\nreturn _M\n--- http_config\n    lua_package_path '$TEST_NGINX_HTML_DIR/?.lua;./?.lua;;';\n    lua_max_running_timers 1;\n--- config\n    location /t {\n        content_by_lua_block {\n            local test = require \"test\"\n\n            local ok, err = ngx.timer.at(0, test.run)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n\n            local ok, err = ngx.timer.at(0, test.run)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- wait: 0.1\n--- no_error_log\n[crit]\n[error]\n--- error_log eval\nqr/\\[alert\\] .*? lua failed to run timer with function defined at @.+\\/test.lua:3: 1 lua_max_running_timers are not enough/\n\n\n\n=== TEST 36: log function location when failed to run a timer with args (lua file)\n--- user_files\n>>> test.lua\nlocal _M = {}\n\nfunction _M.run(premature, arg)\n    ngx.sleep(0.01)\nend\n\nreturn _M\n--- http_config\n    lua_package_path '$TEST_NGINX_HTML_DIR/?.lua;./?.lua;;';\n    lua_max_running_timers 1;\n--- config\n    location /t {\n        content_by_lua_block {\n            local test = require \"test\"\n\n            local ok, err = ngx.timer.at(0, test.run, \"arg\")\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n\n            local ok, err = ngx.timer.at(0, test.run, \"arg\")\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- wait: 0.1\n--- no_error_log\n[crit]\n[error]\n--- error_log eval\nqr/\\[alert\\] .*? lua failed to run timer with function defined at @.+\\/test.lua:3: 1 lua_max_running_timers are not enough/\n"
  },
  {
    "path": "t/107-timer-errors.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 7);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: accessing nginx variables\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                print(\"uri: \", ngx.var.uri)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 2: reading ngx.status\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                print(\"uri: \", ngx.status)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 3: writing ngx.status\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.status = 200\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 4: ngx.req.raw_header\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                print(\"raw header: \", ngx.req.raw_header())\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 5: ngx.req.get_headers\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.get_headers()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 6: ngx.req.set_header\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.set_header(\"Foo\", 32)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 7: ngx.req.clear_header\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.clear_header(\"Foo\")\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 8: ngx.req.set_uri\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.set_uri(\"/foo\")\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 9: ngx.req.set_uri_args\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.set_uri_args(\"foo\")\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 10: ngx.redirect()\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.redirect(\"/foo\")\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the context of ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 11: ngx.exec()\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.exec(\"/foo\")\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the context of ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 12: ngx.say()\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello\")\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the context of ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 13: ngx.print()\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.print(\"hello\")\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the context of ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 14: ngx.flush()\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.flush()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the context of ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 15: ngx.send_headers()\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.send_headers()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the context of ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 16: ngx.req.get_uri_args()\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.get_uri_args()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 17: ngx.req.read_body\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.read_body()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the context of ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 18: ngx.req.discard_body\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.discard_body()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 19: ngx.req.init_body\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.init_body()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 20: ngx.header\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.header.Foo = 3\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 21: ngx.on_abort\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.on_abort(f)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the context of ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 22: ngx.location.capture\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.location.capture(\"/\")\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the context of ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 23: ngx.location.capture_multi\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.location.capture_multi{{\"/\"}}\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the context of ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 24: ngx.req.get_method\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.get_method()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 25: ngx.req.set_method\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.set_method(ngx.HTTP_POST)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 26: ngx.req.http_version\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.http_version()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 27: ngx.req.get_post_args\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.get_post_args()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 28: ngx.req.get_body_data\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.get_body_data()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 29: ngx.req.get_body_file\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.get_body_file()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 30: ngx.req.set_body_data\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.set_body_data(\"hello\")\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 31: ngx.req.set_body_file\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.set_body_file(\"hello\")\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 32: ngx.req.append_body\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.append_body(\"hello\")\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 33: ngx.req.finish_body\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.req.finish_body()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.2\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 34: ngx.headers_sent\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.headers_sent()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the current context/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 35: ngx.eof\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                ngx.eof()\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the context of ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 36: ngx.req.socket\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                local sock, err = ngx.req.socket()\n                if not sock then\n                    ngx.log(ngx.ERR, \"failed to get req sock: \", err)\n                end\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[error\\] .*? runtime error: content_by_lua\\(nginx\\.conf:\\d+\\):3: API disabled in the context of ngx\\.timer/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n"
  },
  {
    "path": "t/108-timer-safe.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 8 + 60);\n\n#no_diff();\nno_long_string();\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir;\n\nworker_connections(1024);\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple at\n--- config\n    location /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"elapsed: \", ngx.now() - begin)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.05)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: 0\\.0(?:4[4-9]|5[0-6])/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 2: simple at (sleep in the timer callback)\n--- config\n    location /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n                ngx.sleep(0.2)\n                print(\"elapsed: \", ngx.now() - begin)\n            end\n            local ok, err = ngx.timer.at(0.5, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.5)\n        ';\n    }\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.5\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[lua\\] .*? my lua timer handler/,\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: 0\\.(?:6[4-9]|7[0-6])/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 3: tcp cosocket in timer handler (short connections)\n--- no_http2\n--- config\n    server_tokens off;\n    location = /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n            local function f()\n                print(\"my lua timer handler\")\n                local sock = ngx.socket.tcp()\n                local port = $TEST_NGINX_SERVER_PORT\n                local ok, err = sock:connect(\"127.0.0.1\", port)\n                if not ok then\n                    fail(\"failed to connect: \", err)\n                    return\n                end\n\n                print(\"connected: \", ok)\n\n                local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                -- req = \"OK\"\n\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    fail(\"failed to send request: \", err)\n                    return\n                end\n\n                print(\"request sent: \", bytes)\n\n                while true do\n                    local line, err, part = sock:receive()\n                    if line then\n                        print(\"received: \", line)\n\n                    else\n                        if err == \"closed\" then\n                            break\n                        end\n                        fail(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                        break\n                    end\n                end\n\n                ok, err = sock:close()\n                print(\"close: \", ok, \" \", err)\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.02)\n        ';\n    }\n\n    location = /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap3 eval: $::GCScript\n--- stap_out2\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 3: ok\ndelete thread 3\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"connected: 1\",\n\"request sent: 57\",\n\"received: HTTP/1.1 200 OK\",\nqr/received: Server: \\S+/,\n\"received: Content-Type: text/plain\",\n\"received: Content-Length: 4\",\n\"received: Connection: close\",\n\"received: foo\",\n\"close: 1 nil\",\n]\n\n\n\n=== TEST 4: tcp cosocket in timer handler (keep-alive connections)\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n\n--- config\n    location = /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n\n                local test = require \"test\"\n                local port = $TEST_NGINX_MEMCACHED_PORT\n                test.go(port)\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.02)\n        ';\n    }\n\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal function fail(...)\n    ngx.log(ngx.ERR, ...)\nend\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        fail(\"failed to connect: \", err)\n        return\n    end\n\n    print(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        fail(\"failed to send request: \", err)\n        return\n    end\n    print(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        print(\"received: \", line)\n\n    else\n        fail(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        fail(\"failed to set reusable: \", err)\n    end\nend\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap3 eval: $::GCScript\n--- stap_out2\ncreate 2 in 1\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nregistered timer\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\nqr/go\\(\\): connected: 1, reused: \\d+/,\n\"go(): request sent: 11\",\n\"go(): received: OK\",\n]\n\n\n\n=== TEST 5: 0 timer\n--- config\n    location /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"elapsed: \", ngx.now() - begin)\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 1: ok\ndelete thread 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nregistered timer\n\n--- wait: 0.02\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: 0(?:[^.]|\\.00)/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 6: udp cosocket in timer handler\n--- config\n    location = /t {\n        content_by_lua '\n            local begin = ngx.now()\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n            local function f()\n                print(\"my lua timer handler\")\n                local socket = ngx.socket\n                -- local socket = require \"socket\"\n\n                local udp = socket.udp()\n\n                local port = $TEST_NGINX_MEMCACHED_PORT\n                udp:settimeout(1000) -- 1 sec\n\n                local ok, err = udp:setpeername(\"127.0.0.1\", port)\n                if not ok then\n                    fail(\"failed to connect: \", err)\n                    return\n                end\n\n                print(\"connected: \", ok)\n\n                local req = \"\\\\0\\\\1\\\\0\\\\0\\\\0\\\\1\\\\0\\\\0flush_all\\\\r\\\\n\"\n                local ok, err = udp:send(req)\n                if not ok then\n                    fail(\"failed to send: \", err)\n                    return\n                end\n\n                local data, err = udp:receive()\n                if not data then\n                    fail(\"failed to receive data: \", err)\n                    return\n                end\n                print(\"received \", #data, \" bytes: \", data)\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.05)\n        ';\n    }\n\n    location = /foo {\n        content_by_lua 'ngx.say(\"foo\")';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nregistered timer\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"connected: 1\",\n\"received 12 bytes: \\x{00}\\x{01}\\x{00}\\x{00}\\x{00}\\x{01}\\x{00}\\x{00}OK\\x{0d}\\x{0a}\"\n]\n\n\n\n=== TEST 7: simple at (sleep in the timer callback) - log_by_lua\n--- config\n    location /t {\n        echo hello world;\n        echo_sleep 0.07;\n        log_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n                ngx.sleep(0.02)\n                print(\"elapsed: \", ngx.now() - begin)\n            end\n            local ok, err = ngx.timer.at(0.05, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set timer: \", err)\n                return\n            end\n            print(\"registered timer\")\n        ';\n    }\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nhello world\n\n--- wait: 0.15\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"registered timer\",\nqr/\\[lua\\] .*? my lua timer handler/,\nqr/\\[lua\\] log_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: 0\\.0(?:6[4-9]|7[0-9]|8[0-6])/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 8: tcp cosocket in timer handler (keep-alive connections) - log_by_lua\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n\n--- config\n    location = /t {\n        echo hello;\n        echo_sleep 0.01;\n        log_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n\n                local test = require \"test\"\n                local port = $TEST_NGINX_MEMCACHED_PORT\n                test.go(port)\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set timer: \", err)\n                return\n            end\n            print(\"registered timer\")\n        ';\n    }\n\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal function fail(...)\n    ngx.log(ngx.ERR, ...)\nend\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        fail(\"failed to connect: \", err)\n        return\n    end\n\n    print(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        fail(\"failed to send request: \", err)\n        return\n    end\n    print(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        print(\"received: \", line)\n\n    else\n        fail(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        fail(\"failed to set reusable: \", err)\n    end\nend\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nhello\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"registered timer\",\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\nqr/go\\(\\): connected: 1, reused: \\d+/,\n\"go(): request sent: 11\",\n\"go(): received: OK\",\n]\n\n\n\n=== TEST 9: tcp cosocket in timer handler (keep-alive connections) - header_filter_by_lua\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n\n--- config\n    location = /t {\n        echo hello;\n        echo_sleep 0.01;\n        header_filter_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n\n                local test = require \"test\"\n                local port = $TEST_NGINX_MEMCACHED_PORT\n                test.go(port)\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set timer: \", err)\n                return\n            end\n            print(\"registered timer\")\n        ';\n    }\n\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal function fail(...)\n    ngx.log(ngx.ERR, ...)\nend\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        fail(\"failed to connect: \", err)\n        return\n    end\n\n    print(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        fail(\"failed to send request: \", err)\n        return\n    end\n    print(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        print(\"received: \", line)\n\n    else\n        fail(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        fail(\"failed to set reusable: \", err)\n    end\nend\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap3\nglobal count = 0\nF(ngx_http_lua_header_filter) {\n    if (count++ == 10) {\n        println(\"header filter\")\n        print_ubacktrace()\n    }\n}\n\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\nhello\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"registered timer\",\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\nqr/go\\(\\): connected: 1, reused: \\d+/,\n\"go(): request sent: 11\",\n\"go(): received: OK\",\n]\n\n\n\n=== TEST 10: tcp cosocket in timer handler (keep-alive connections) - body_filter_by_lua\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n\n--- config\n    location = /t {\n        echo_sleep 0.01;\n        echo hello;\n        body_filter_by_lua '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n\n                local test = require \"test\"\n                local port = $TEST_NGINX_MEMCACHED_PORT\n                test.go(port)\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set timer: \", err)\n                return\n            end\n            print(\"registered timer\")\n        ';\n    }\n\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal function fail(...)\n    ngx.log(ngx.ERR, ...)\nend\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        fail(\"failed to connect: \", err)\n        return\n    end\n\n    print(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        fail(\"failed to send request: \", err)\n        return\n    end\n    print(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        print(\"received: \", line)\n\n    else\n        fail(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        fail(\"failed to set reusable: \", err)\n    end\nend\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap3\nglobal count = 0\nF(ngx_http_lua_header_filter) {\n    if (count++ == 10) {\n        println(\"header filter\")\n        print_ubacktrace()\n    }\n}\n\n--- stap eval: $::GCScript\n--- stap_out_like chop\ncreate 2 in 1\ncreate 3 in 1\n(?:terminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\n|terminate 3: ok\ndelete thread 3\nterminate 2: ok\ndelete thread 2)$\n\n--- response_body\nhello\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"registered timer\",\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\nqr/go\\(\\): connected: 1, reused: \\d+/,\n\"go(): request sent: 11\",\n\"go(): received: OK\",\n]\n\n\n\n=== TEST 11: tcp cosocket in timer handler (keep-alive connections) - set_by_lua\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n\n--- config\n    location = /t {\n        set_by_lua $a '\n            local begin = ngx.now()\n            local function f()\n                print(\"my lua timer handler\")\n\n                local test = require \"test\"\n                local port = $TEST_NGINX_MEMCACHED_PORT\n                test.go(port)\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set timer: \", err)\n                return\n            end\n            print(\"registered timer\")\n            return 32\n        ';\n        echo $a;\n        echo_sleep 0.01;\n    }\n\n--- user_files\n>>> test.lua\nmodule(\"test\", package.seeall)\n\nlocal function fail(...)\n    ngx.log(ngx.ERR, ...)\nend\n\nfunction go(port)\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        fail(\"failed to connect: \", err)\n        return\n    end\n\n    print(\"connected: \", ok, \", reused: \", sock:getreusedtimes())\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        fail(\"failed to send request: \", err)\n        return\n    end\n    print(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        print(\"received: \", line)\n\n    else\n        fail(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        fail(\"failed to set reusable: \", err)\n    end\nend\n\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- stap3\nglobal count = 0\nF(ngx_http_lua_header_filter) {\n    if (count++ == 10) {\n        println(\"header filter\")\n        print_ubacktrace()\n    }\n}\n\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 2: ok\ndelete thread 2\n\n--- response_body\n32\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"registered timer\",\nqr/\\[lua\\] .*? my lua timer handler/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\nqr/go\\(\\): connected: 1, reused: \\d+/,\n\"go(): request sent: 11\",\n\"go(): received: OK\",\n]\n\n\n\n=== TEST 12: coroutine API\n--- config\n    location /t {\n        content_by_lua '\n            local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n            local function f()\n                function f()\n                    local cnt = 0\n                    for i = 1, 20 do\n                        print(\"cnt = \", cnt)\n                        cy()\n                        cnt = cnt + 1\n                    end\n                end\n\n                local c = cc(f)\n                for i=1,3 do\n                    cr(c)\n                    print(\"after resume, i = \", i)\n                end\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.01)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 2\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"cnt = 0\",\n\"after resume, i = 1\",\n\"cnt = 1\",\n\"after resume, i = 2\",\n\"cnt = 2\",\n\"after resume, i = 3\",\n]\n\n\n\n=== TEST 13: ngx.thread API\n--- config\n    location /t {\n        content_by_lua '\n            local function fail (...)\n                ngx.log(ngx.ERR, ...)\n            end\n            local function handle()\n                local function f()\n                    print(\"hello in thread\")\n                    return \"done\"\n                end\n\n                local t, err = ngx.thread.spawn(f)\n                if not t then\n                    fail(\"failed to spawn thread: \", err)\n                    return\n                end\n\n                print(\"thread created: \", coroutine.status(t))\n\n                collectgarbage()\n\n                local ok, res = ngx.thread.wait(t)\n                if not ok then\n                    fail(\"failed to run thread: \", res)\n                    return\n                end\n\n                print(\"wait result: \", res)\n            end\n            local ok, err = ngx.timer.at(0.01, handle)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.02)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nterminate 3: ok\ndelete thread 3\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"hello in thread\",\n\"thread created: zombie\",\n\"wait result: done\",\n]\n\n\n\n=== TEST 14: shared dict\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                local dogs = ngx.shared.dogs\n                dogs:set(\"foo\", 32)\n                dogs:set(\"bah\", 10502)\n                local val = dogs:get(\"foo\")\n                print(\"get foo: \", val, \" \", type(val))\n                val = dogs:get(\"bah\")\n                print(\"get bah: \", val, \" \", type(val))\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.02)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"get foo: 32 number\",\n\"get bah: 10502 number\",\n]\n\n\n\n=== TEST 15: ngx.exit(0)\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                local function g()\n                    print(\"BEFORE ngx.exit\")\n                    ngx.exit(0)\n                end\n                g()\n                print(\"CANNOT REACH HERE\")\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.01)\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[alert]\n[crit]\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"BEFORE ngx.exit\",\n]\n--- no_error_log\nCANNOT REACH HERE\nAPI disabled\n\n\n\n=== TEST 16: ngx.exit(403)\n--- config\n    location /t {\n        content_by_lua '\n            local function f()\n                local function g()\n                    print(\"BEFORE ngx.exit\")\n                    ngx.exit(403)\n                end\n                g()\n                print(\"CANNOT REACH HERE\")\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.01)\n        ';\n    }\n--- request\nGET /t\n--- stap2\nF(ngx_http_lua_timer_handler) {\n    println(\"lua timer handler\")\n}\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\nCANNOT REACH HERE\nAPI disabled\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"BEFORE ngx.exit\",\n]\n\n\n\n=== TEST 17: exit in user thread (entry thread is still pending on ngx.sleep)\n--- config\n    location /t {\n        content_by_lua '\n            local function handle()\n                local function f()\n                    print(\"hello in thread\")\n                    ngx.sleep(0.1)\n                    ngx.exit(0)\n                end\n\n                print(\"BEFORE thread spawn\")\n                ngx.thread.spawn(f)\n                print(\"AFTER thread spawn\")\n                ngx.sleep(1)\n                print(\"entry thread END\")\n            end\n            local ok, err = ngx.timer.at(0.01, handle)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.12)\n        ';\n    }\n--- request\nGET /t\n--- stap eval\n<<'_EOC_' . $::GCScript;\n\nglobal timers\n\nF(ngx_http_free_request) {\n    println(\"free request\")\n}\n\nM(timer-add) {\n    if ($arg2 == 1000 || $arg2 == 100) {\n        timers[$arg1] = $arg2\n        printf(\"add timer %d\\n\", $arg2)\n    }\n}\n\nM(timer-del) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"delete timer %d\\n\", tm)\n        delete timers[$arg1]\n    }\n    /*\n    if (tm == 1000) {\n        print_ubacktrace()\n    }\n    */\n}\n\nM(timer-expire) {\n    tm = timers[$arg1]\n    if (tm == 1000 || tm == 100) {\n        printf(\"expire timer %d\\n\", timers[$arg1])\n        delete timers[$arg1]\n    }\n}\n\nF(ngx_http_lua_sleep_cleanup) {\n    println(\"lua sleep cleanup\")\n}\n_EOC_\n\n--- stap_out\ncreate 2 in 1\ncreate 3 in 2\nspawn user thread 3 in 2\nadd timer 100\nadd timer 1000\nexpire timer 100\nterminate 3: ok\ndelete thread 3\nlua sleep cleanup\ndelete timer 1000\ndelete thread 2\nterminate 1: ok\ndelete thread 1\nfree request\n\n--- response_body\nregistered timer\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\nAPI disabled\nentry thread END\n\n--- error_log eval\n[\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"BEFORE thread spawn\",\n\"hello in thread\",\n\"AFTER thread spawn\",\n]\n\n\n\n=== TEST 18: chained timers (non-zero delay)\n--- config\n    location /t {\n        content_by_lua '\n            local s = \"\"\n\n            local function fail(...)\n                ngx.log(ngx.ERR, ...)\n            end\n\n            local function g()\n                s = s .. \"[g]\"\n                print(\"trace: \", s)\n            end\n\n            local function f()\n                local ok, err = ngx.timer.at(0.01, g)\n                if not ok then\n                    fail(\"failed to set timer: \", err)\n                    return\n                end\n                s = s .. \"[f]\"\n            end\n            local ok, err = ngx.timer.at(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            s = \"[m]\"\n            ngx.sleep(0.03)\n        ';\n    }\n--- request\nGET /t\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\ncreate 3 in 2\nterminate 2: ok\ndelete thread 2\nterminate 3: ok\ndelete thread 3\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nregistered timer\n\n--- wait: 0.1\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n--- error_log\nlua ngx.timer expired\nhttp lua close fake http connection\ntrace: [m][f][g]\n"
  },
  {
    "path": "t/109-timer-hup.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_CHECK_LEAK}) {\n        $SkipReason = \"unavailable for the hup tests\";\n\n    } elsif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n        #os.execute(\"kill -HUP \" .. pid)\n        $SkipReason = \"send HUP relaod signal by self make two workers with same id\";\n\n    } else {\n        $ENV{TEST_NGINX_USE_HUP} = 1;\n        undef $ENV{TEST_NGINX_USE_STAP};\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\n\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 81;\n\n#no_diff();\nno_long_string();\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir;\n\nworker_connections(1024);\nrun_tests();\n\n__DATA__\n\n=== TEST 1: single timer\n--- config\n    location /t {\n        content_by_lua '\n            local f, err = io.open(\"$TEST_NGINX_SERVER_ROOT/logs/nginx.pid\", \"r\")\n            if not f then\n                ngx.say(\"failed to open nginx.pid: \", err)\n                return\n            end\n\n            local pid = f:read()\n            -- ngx.say(\"master pid: [\", pid, \"]\")\n\n            f:close()\n\n            local i = 0\n            local function f(premature)\n                i = i + 1\n                print(\"timer prematurely expired: \", premature)\n                print(\"in callback: hello, \", i)\n            end\n            local ok, err = ngx.timer.at(3, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            os.execute(\"kill -HUP \" .. pid)\n        ';\n    }\n--- request\nGET /t\n\n--- response_body\nregistered timer\n\n--- wait: 0.3\n--- no_error_log\n[error]\n[alert]\n[crit]\nin callback: hello, 2\ntimer prematurely expired: false\n\n--- error_log\nlua abort pending timers\nlua ngx.timer expired\nhttp lua close fake http connection\nin callback: hello, 1\ntimer prematurely expired: true\n\n\n\n=== TEST 2: multiple timers\n--- config\n    location /t {\n        content_by_lua '\n            local f, err = io.open(\"$TEST_NGINX_SERVER_ROOT/logs/nginx.pid\", \"r\")\n            if not f then\n                ngx.say(\"failed to open nginx.pid: \", err)\n                return\n            end\n\n            local pid = f:read()\n            -- ngx.say(\"master pid: [\", pid, \"]\")\n\n            f:close()\n\n            local i = 0\n            local function f(premature)\n                i = i + 1\n                print(\"timer prematurely expired: \", premature)\n                print(\"in callback: hello, \", i, \"!\")\n            end\n            for i = 1, 10 do\n                local ok, err = ngx.timer.at(3, f)\n                if not ok then\n                    ngx.say(\"failed to set timer: \", err)\n                    return\n                end\n            end\n            ngx.say(\"registered timers\")\n            os.execute(\"kill -HUP \" .. pid)\n        ';\n    }\n--- request\nGET /t\n\n--- response_body\nregistered timers\n\n--- wait: 0.3\n--- no_error_log\n[error]\n[alert]\n[crit]\nin callback: hello, 11!\ntimer prematurely expired: false\n\n--- error_log\nlua abort pending timers\nlua ngx.timer expired\nhttp lua close fake http connection\nin callback: hello, 1!\nin callback: hello, 2!\nin callback: hello, 3!\nin callback: hello, 4!\nin callback: hello, 5!\nin callback: hello, 6!\nin callback: hello, 7!\nin callback: hello, 8!\nin callback: hello, 9!\nin callback: hello, 10!\ntimer prematurely expired: true\n\n\n\n=== TEST 3: trying to add new timer after HUP reload\n--- config\n    location /t {\n        content_by_lua '\n            local f, err = io.open(\"$TEST_NGINX_SERVER_ROOT/logs/nginx.pid\", \"r\")\n            if not f then\n                ngx.say(\"failed to open nginx.pid: \", err)\n                return\n            end\n\n            local pid = f:read()\n            -- ngx.say(\"master pid: [\", pid, \"]\")\n\n            f:close()\n\n            local function f(premature)\n                print(\"timer prematurely expired: \", premature)\n                local ok, err = ngx.timer.at(3, f)\n                if not ok then\n                    print(\"failed to register a new timer after reload: \", err)\n                else\n                    print(\"registered a new timer after reload\")\n                end\n            end\n            local ok, err = ngx.timer.at(3, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            os.execute(\"kill -HUP \" .. pid)\n        ';\n    }\n--- request\nGET /t\n\n--- response_body\nregistered timer\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\nin callback: hello, 2\ntimer prematurely expired: false\n\n--- error_log\nlua abort pending timers\nlua ngx.timer expired\nhttp lua close fake http connection\ntimer prematurely expired: true\nfailed to register a new timer after reload: process exiting, context: ngx.timer\n\n\n\n=== TEST 4: trying to add new timer after HUP reload\n--- config\n    location /t {\n        content_by_lua '\n            local f, err = io.open(\"$TEST_NGINX_SERVER_ROOT/logs/nginx.pid\", \"r\")\n            if not f then\n                ngx.say(\"failed to open nginx.pid: \", err)\n                return\n            end\n\n            local pid = f:read()\n            -- ngx.say(\"master pid: [\", pid, \"]\")\n\n            f:close()\n\n            local function g(premature)\n                print(\"g: timer prematurely expired: \", premature)\n                print(\"g: exiting=\", ngx.worker.exiting())\n            end\n\n            local function f(premature)\n                print(\"f: timer prematurely expired: \", premature)\n                print(\"f: exiting=\", ngx.worker.exiting())\n                local ok, err = ngx.timer.at(0, g)\n                if not ok then\n                    print(\"f: failed to register a new timer after reload: \", err)\n                else\n                    print(\"f: registered a new timer after reload\")\n                end\n            end\n            local ok, err = ngx.timer.at(3, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            os.execute(\"kill -HUP \" .. pid)\n        ';\n    }\n--- request\nGET /t\n\n--- response_body\nregistered timer\n\n--- wait: 0.2\n--- no_error_log\n[error]\n[alert]\n[crit]\nin callback: hello, 2\nfailed to register a new timer after reload\n\n--- error_log\nlua abort pending timers\nlua ngx.timer expired\nhttp lua close fake http connection\nf: timer prematurely expired: true\nf: registered a new timer after reload\nf: exiting=true\ng: timer prematurely expired: false\ng: exiting=true\n\n\n\n=== TEST 5: HUP reload should abort pending timers\n--- config\n    location /t {\n        content_by_lua '\n            local f, err = io.open(\"$TEST_NGINX_SERVER_ROOT/logs/nginx.pid\", \"r\")\n            if not f then\n                ngx.say(\"failed to open nginx.pid: \", err)\n                return\n            end\n\n            local pid = f:read()\n            -- ngx.say(\"master pid: [\", pid, \"]\")\n\n            f:close()\n\n            local function f(premature)\n                print(\"f: timer prematurely expired: \", premature)\n                print(\"f: exiting=\", ngx.worker.exiting())\n            end\n\n            for i = 1, 100 do\n                local ok, err = ngx.timer.at(3 + i, f)\n                if not ok then\n                    ngx.say(\"failed to set timer: \", err)\n                    return\n                end\n            end\n            ngx.say(\"ok\")\n            os.execute(\"kill -HUP \" .. pid)\n        ';\n    }\n--- request\nGET /t\n\n--- response_body\nok\n\n--- wait: 0.5\n--- no_error_log\n[error]\n[alert]\n[crit]\nin callback: hello, 2\nfailed to register a new timer after reload\n\n--- grep_error_log eval: qr/lua found \\d+ pending timers/\n--- grep_error_log_out\nlua found 100 pending timers\n\n\n\n=== TEST 6: HUP reload should abort pending timers (coroutine + cosocket)\n--- http_config\n    lua_shared_dict test_dict 1m;\n\n    server {\n        listen $TEST_NGINX_RAND_PORT_1;\n        location = /foo {\n            echo 'foo';\n        }\n    }\n\n--- config\n    location /t {\n        content_by_lua '\n            local http_req = {\"GET /foo HTTP/1.1\", \"Host: localhost:1234\", \"\", \"\"}\n            http_req = table.concat(http_req, \"\\\\r\\\\n\")\n\n            -- Connect the socket\n            local sock = ngx.socket.tcp()\n            local ok,err = sock:connect(\"127.0.0.1\", $TEST_NGINX_RAND_PORT_1)\n            if not ok then\n                ngx.log(ngx.ERR, err)\n            end\n\n            -- Send the request\n            local ok,err = sock:send(http_req)\n\n            -- Get Headers\n            repeat\n                local line, err = sock:receive(\"*l\")\n            until not line or string.find(line, \"^%s*$\")\n\n            local function foo()\n                repeat\n                    -- Get and read chunk\n                    local line, err = sock:receive(\"*l\")\n                    local len = tonumber(line)\n                    if len > 0 then\n                        local chunk, err = sock:receive(len)\n                        coroutine.yield(chunk)\n                        sock:receive(2)\n                    else\n                        -- Read last newline\n                        sock:receive(2)\n                    end\n                until len == 0\n            end\n\n            local co = coroutine.create(foo)\n            repeat\n                local chunk = select(2,coroutine.resume(co))\n            until chunk == nil\n\n            -- Breaks the timer\n            sock:setkeepalive()\n            ngx.say(\"ok\")\n        ';\n\n        log_by_lua '\n            local background_thread\n            background_thread = function(premature)\n                ngx.log(ngx.DEBUG, premature)\n                if premature then\n                    ngx.shared[\"test_dict\"]:delete(\"background_flag\")\n                    return\n                end\n                local ok, err = ngx.timer.at(1, background_thread)\n\n                local f, err = io.open(\"$TEST_NGINX_SERVER_ROOT/logs/nginx.pid\", \"r\")\n                if not f then\n                    ngx.say(\"failed to open nginx.pid: \", err)\n                    return\n                end\n                local pid = f:read()\n                -- ngx.say(\"master pid: [\", pid, \"]\")\n                f:close()\n\n                os.execute(\"kill -HUP \" .. pid)\n            end\n            local dict = ngx.shared[\"test_dict\"]\n\n            if dict:get(\"background_flag\") == nil then\n                local ok, err = ngx.timer.at(0, background_thread)\n                if ok then\n                    dict:set(\"test_dict\", 1)\n                end\n            end\n        ';\n    }\n--- request\nGET /t\n\n--- response_body\nok\n\n--- wait: 0.3\n--- no_error_log\n[error]\n[alert]\n[crit]\nin callback: hello, 2\nfailed to register a new timer after reload\n\n--- grep_error_log eval: qr/lua found \\d+ pending timers/\n--- grep_error_log_out\nlua found 1 pending timers\n\n\n\n=== TEST 7: HUP reload should abort pending timers (fuzz test)\n--- http_config\n    lua_max_pending_timers 8192;\n\n--- config\n    location /t {\n        content_by_lua '\n            local job = function(premature, kill)\n                if premature then\n                    return\n                end\n\n                if kill then\n                    local f, err = io.open(\"$TEST_NGINX_SERVER_ROOT/logs/nginx.pid\", \"r\")\n                    if not f then\n                        ngx.log(ngx.ERR, \"failed to open nginx.pid: \", err)\n                        return\n                    end\n                    local pid = f:read()\n                    -- ngx.say(\"master pid: [\", pid, \"]\")\n                    f:close()\n\n                    os.execute(\"kill -HUP \" .. pid)\n                end\n            end\n\n            math.randomseed(ngx.time())\n            local rand = math.random\n            local newtimer = ngx.timer.at\n            for i = 1, 8191 do\n                local delay = rand(4096)\n                local ok, err = newtimer(delay, job, false)\n                if not ok then\n                    ngx.say(\"failed to create timer at \", delay, \": \", err)\n                    return\n                end\n            end\n            local ok, err = newtimer(0, job, true)\n            if not ok then\n                ngx.say(\"failed to create the killer timer: \", err)\n                return\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\nGET /t\n\n--- response_body\nok\n\n--- wait: 0.3\n--- no_error_log\n[error]\n[alert]\n\n--- grep_error_log eval: qr/lua found \\d+ pending timers/\n--- grep_error_log_out\nlua found 8191 pending timers\n--- timeout: 20\n"
  },
  {
    "path": "t/110-etag.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: If-None-Match true\n--- config\n    location /t {\n        content_by_lua '\n            ngx.header[\"ETag\"] = \"123456789\"\n            ngx.header.last_modified = \"Thu, 10 May 2012 07:50:59 GMT\"\n            ngx.say(ngx.var.http_if_none_match)\n        ';\n    }\n--- request\nGET /t\n--- more_headers\nIf-None-Match: 123456789\nIf-Modified-Since: Thu, 10 May 2012 07:50:59 GMT\n--- response_body\n--- error_code: 304\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: If-None-Match false\n--- config\n    location /t {\n        etag on;\n        content_by_lua '\n            ngx.header[\"ETag\"] = \"123456789\"\n            ngx.header.last_modified = \"Thu, 10 May 2012 07:50:59 GMT\"\n            ngx.say(ngx.var.http_if_none_match)\n        ';\n    }\n--- request\nGET /t\n--- more_headers\nIf-None-Match: 123456780\nIf-Modified-Since: Thu, 10 May 2012 07:50:59 GMT\n--- response_body\n123456780\n--- no_error_log\n[error]\n--- skip_nginx: 3: < 1.3.3\n\n\n\n=== TEST 3: Etag clear\n--- config\n    location /t {\n        etag on;\n        content_by_lua '\n            ngx.header[\"ETag\"] = \"123456789\"\n            ngx.header.last_modified = \"Thu, 10 May 2012 07:50:59 GMT\"\n            ngx.header[\"ETag\"] = nil\n            ngx.say(ngx.var.http_if_none_match)\n        ';\n    }\n--- request\nGET /t\n--- more_headers\nIf-None-Match: 123456789\nIf-Modified-Since: Thu, 10 May 2012 07:50:59 GMT\n--- response_body\n123456789\n--- no_error_log\n[error]\n--- skip_nginx: 3: < 1.3.3\n"
  },
  {
    "path": "t/111-req-header-ua.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (4 * blocks());\n\n#no_diff();\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: clear Opera user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", nil)\n\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- more_headers\nUser-Agent: Opera/9.80 (Macintosh; Intel Mac OS X 10.7.4; U; en) Presto/2.10.229 Version/11.62\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: opera: %d\\n\", $r->headers_in->opera)\n}\n\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: opera: %d\\n\", $r->headers_in->opera)\n}\n\n--- stap_out\nrewrite: opera: 1\ncontent: opera: 0\n\n--- response_body\nUser-Agent: \n--- no_error_log\n[error]\n\n\n\n=== TEST 2: clear MSIE 4 user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", nil)\n\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- more_headers\nUser-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows NT 5.0)\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\n--- stap_out\nrewrite: msie=1 msie6=1\ncontent: msie=0 msie6=0\n\n--- response_body\nUser-Agent: \n--- no_error_log\n[error]\n\n\n\n=== TEST 3: set custom MSIE 4 user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", \"Mozilla/4.0 (compatible; MSIE 4.01; Windows NT 5.0)\")\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\n--- stap_out\nrewrite: msie=0 msie6=0\ncontent: msie=1 msie6=1\n\n--- response_body\nUser-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows NT 5.0)\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: clear MSIE 5 user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", nil)\n\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- more_headers\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows 95; MSIECrawler)\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\n--- stap_out\nrewrite: msie=1 msie6=1\ncontent: msie=0 msie6=0\n\n--- response_body\nUser-Agent: \n--- no_error_log\n[error]\n\n\n\n=== TEST 5: set custom MSIE 5 user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", \"Mozilla/4.0 (compatible; MSIE 5.01; Windows 95; MSIECrawler)\")\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\n--- stap_out\nrewrite: msie=0 msie6=0\ncontent: msie=1 msie6=1\n\n--- response_body\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows 95; MSIECrawler)\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: clear MSIE 6 (without SV1) user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", nil)\n\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- more_headers\nUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Google Wireless Transcoder;)\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\n--- stap_out\nrewrite: msie=1 msie6=1\ncontent: msie=0 msie6=0\n\n--- response_body\nUser-Agent: \n--- no_error_log\n[error]\n\n\n\n=== TEST 7: set custom MSIE 6 (without SV1) user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", \"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Google Wireless Transcoder;)\")\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\n--- stap_out\nrewrite: msie=0 msie6=0\ncontent: msie=1 msie6=1\n\n--- response_body\nUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Google Wireless Transcoder;)\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: clear MSIE 6 (with SV1) user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", nil)\n\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- more_headers\nUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1)\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\n--- stap_out\nrewrite: msie=1 msie6=0\ncontent: msie=0 msie6=0\n\n--- response_body\nUser-Agent: \n--- no_error_log\n[error]\n\n\n\n=== TEST 9: set custom MSIE 6 (with SV1) user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", \"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1)\")\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\n--- stap_out\nrewrite: msie=0 msie6=0\ncontent: msie=1 msie6=0\n\n--- response_body\nUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1)\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: set custom MSIE 7 user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; winfx; .NET CLR 1.1.4322; .NET CLR 2.0.50727; Zune 2.0)\")\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: msie=%d msie6=%d\\n\",\n           $r->headers_in->msie,\n           $r->headers_in->msie6)\n}\n\n--- stap_out\nrewrite: msie=0 msie6=0\ncontent: msie=1 msie6=0\n\n--- response_body\nUser-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; winfx; .NET CLR 1.1.4322; .NET CLR 2.0.50727; Zune 2.0)\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: clear Gecko user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", nil)\n\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- more_headers\nUser-Agent: Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: gecko: %d\\n\", $r->headers_in->gecko)\n}\n\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: gecko: %d\\n\", $r->headers_in->gecko)\n}\n\n--- stap_out\nrewrite: gecko: 1\ncontent: gecko: 0\n\n--- response_body\nUser-Agent: \n--- no_error_log\n[error]\n\n\n\n=== TEST 12: set custom Gecko user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", \"Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0\")\n\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: gecko: %d\\n\", $r->headers_in->gecko)\n}\n\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: gecko: %d\\n\", $r->headers_in->gecko)\n}\n\n--- stap_out\nrewrite: gecko: 0\ncontent: gecko: 1\n\n--- response_body\nUser-Agent: Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: clear Chrome user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", nil)\n\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- more_headers\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.151 Safari/535.19\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: chrome: %d\\n\", $r->headers_in->chrome)\n}\n\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: chrome: %d\\n\", $r->headers_in->chrome)\n}\n\n--- stap_out\nrewrite: chrome: 1\ncontent: chrome: 0\n\n--- response_body\nUser-Agent: \n--- no_error_log\n[error]\n\n\n\n=== TEST 14: set custom Chrome user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.151 Safari/535.19\")\n\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: chrome: %d\\n\", $r->headers_in->chrome)\n}\n\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: chrome: %d\\n\", $r->headers_in->chrome)\n}\n\n--- stap_out\nrewrite: chrome: 0\ncontent: chrome: 1\n\n--- response_body\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.151 Safari/535.19\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: clear Safari (Mac OS X) user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", nil)\n\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- more_headers\nUser-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: safari: %d\\n\", $r->headers_in->safari)\n}\n\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: safari: %d\\n\", $r->headers_in->safari)\n}\n\n--- stap_out\nrewrite: safari: 1\ncontent: safari: 0\n\n--- response_body\nUser-Agent: \n--- no_error_log\n[error]\n\n\n\n=== TEST 16: set custom Safari user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", \"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8\")\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: safari: %d\\n\", $r->headers_in->safari)\n}\n\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: safari: %d\\n\", $r->headers_in->safari)\n}\n\n--- stap_out\nrewrite: safari: 0\ncontent: safari: 1\n\n--- response_body\nUser-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: clear Konqueror user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", nil)\n\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- more_headers\nUser-Agent: Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.10 (like Gecko) (Kubuntu)\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: konqueror: %d\\n\", $r->headers_in->konqueror)\n}\n\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: konqueror: %d\\n\", $r->headers_in->konqueror)\n}\n\n--- stap_out\nrewrite: konqueror: 1\ncontent: konqueror: 0\n\n--- response_body\nUser-Agent: \n--- no_error_log\n[error]\n\n\n\n=== TEST 18: set custom Konqueror user-agent\n--- config\n    location /t {\n        rewrite_by_lua '\n            ngx.req.set_header(\"User-Agent\", \"Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.10 (like Gecko) (Kubuntu)\")\n        ';\n        echo \"User-Agent: $http_user_agent\";\n    }\n\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: konqueror: %d\\n\", $r->headers_in->konqueror)\n}\n\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: konqueror: %d\\n\", $r->headers_in->konqueror)\n}\n\n--- stap_out\nrewrite: konqueror: 0\ncontent: konqueror: 1\n\n--- response_body\nUser-Agent: Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.10 (like Gecko) (Kubuntu)\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/112-req-header-conn.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (4 * blocks());\n\n#no_diff();\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: clear the Connection req header\n--- config\n    location /req-header {\n        rewrite_by_lua '\n            ngx.req.set_header(\"Connection\", nil);\n        ';\n\n        echo \"connection: $http_connection\";\n    }\n--- request\nGET /req-header\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: conn type: %d\\n\", $r->headers_in->connection_type)\n}\n\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: conn type: %d\\n\", $r->headers_in->connection_type)\n}\n\n--- stap_out\nrewrite: conn type: 1\ncontent: conn type: 0\n\n--- response_body\nconnection: \n--- no_error_log\n[error]\n\n\n\n=== TEST 2: set custom Connection req header (close)\n--- config\n    location /req-header {\n        rewrite_by_lua '\n            ngx.req.set_header(\"Connection\", \"CLOSE\");\n        ';\n\n        echo \"connection: $http_connection\";\n    }\n--- request\nGET /req-header\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: conn type: %d\\n\", $r->headers_in->connection_type)\n}\n\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: conn type: %d\\n\", $r->headers_in->connection_type)\n}\n\n--- stap_out\nrewrite: conn type: 1\ncontent: conn type: 1\n\n--- response_body\nconnection: CLOSE\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: set custom Connection req header (keep-alive)\n--- config\n    location /req-header {\n        rewrite_by_lua '\n            ngx.req.set_header(\"Connection\", \"keep-alive\");\n        ';\n\n        echo \"connection: $http_connection\";\n    }\n--- request\nGET /req-header\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: conn type: %d\\n\", $r->headers_in->connection_type)\n}\n\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: conn type: %d\\n\", $r->headers_in->connection_type)\n}\n\n--- stap_out\nrewrite: conn type: 1\ncontent: conn type: 2\n\n--- response_body\nconnection: keep-alive\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: set custom Connection req header (bad)\n--- config\n    location /req-header {\n        rewrite_by_lua '\n            ngx.req.set_header(\"Connection\", \"bad\");\n        ';\n\n        echo \"connection: $http_connection\";\n    }\n--- request\nGET /req-header\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: conn type: %d\\n\", $r->headers_in->connection_type)\n}\n\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: conn type: %d\\n\", $r->headers_in->connection_type)\n}\n\n--- stap_out\nrewrite: conn type: 1\ncontent: conn type: 0\n\n--- response_body\nconnection: bad\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/113-req-header-cookie.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (3 * blocks() + 6);\n\n#no_diff();\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: clear cookie (with existing cookies)\n--- config\n    location /t {\n        rewrite_by_lua '\n           ngx.req.set_header(\"Cookie\", nil)\n        ';\n        echo \"Cookie foo: $cookie_foo\";\n        echo \"Cookie baz: $cookie_baz\";\n        echo \"Cookie: $http_cookie\";\n    }\n--- request\nGET /t\n--- more_headers\nCookie: foo=bar\nCookie: baz=blah\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: cookies: %d\\n\", $r->headers_in->cookies->nelts)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: cookies: %d\\n\", $r->headers_in->cookies->nelts)\n}\n\n--- stap_out\nrewrite: cookies: 2\ncontent: cookies: 0\n\n--- response_body\nCookie foo: \nCookie baz: \nCookie: \n\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: clear cookie (without existing cookies)\n--- config\n    location /t {\n        rewrite_by_lua '\n           ngx.req.set_header(\"Cookie\", nil)\n        ';\n        echo \"Cookie foo: $cookie_foo\";\n        echo \"Cookie baz: $cookie_baz\";\n        echo \"Cookie: $http_cookie\";\n    }\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: cookies: %d\\n\", $r->headers_in->cookies->nelts)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: cookies: %d\\n\", $r->headers_in->cookies->nelts)\n}\n\n--- stap_out\nrewrite: cookies: 0\ncontent: cookies: 0\n\n--- response_body\nCookie foo: \nCookie baz: \nCookie: \n\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: set one custom cookie (with existing cookies)\n--- config\n    location /t {\n        rewrite_by_lua '\n           ngx.req.set_header(\"Cookie\", \"boo=123\")\n        ';\n        echo \"Cookie foo: $cookie_foo\";\n        echo \"Cookie baz: $cookie_baz\";\n        echo \"Cookie boo: $cookie_boo\";\n        echo \"Cookie: $http_cookie\";\n    }\n--- request\nGET /t\n--- more_headers\nCookie: foo=bar\nCookie: baz=blah\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: cookies: %d\\n\", $r->headers_in->cookies->nelts)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: cookies: %d\\n\", $r->headers_in->cookies->nelts)\n}\n\n--- stap_out\nrewrite: cookies: 2\ncontent: cookies: 1\n\n--- response_body\nCookie foo: \nCookie baz: \nCookie boo: 123\nCookie: boo=123\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: set one custom cookie (without existing cookies)\n--- config\n    location /t {\n        rewrite_by_lua '\n           ngx.req.set_header(\"Cookie\", \"boo=123\")\n        ';\n        echo \"Cookie foo: $cookie_foo\";\n        echo \"Cookie baz: $cookie_baz\";\n        echo \"Cookie boo: $cookie_boo\";\n        echo \"Cookie: $http_cookie\";\n    }\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: cookies: %d\\n\", $r->headers_in->cookies->nelts)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: cookies: %d\\n\", $r->headers_in->cookies->nelts)\n}\n\n--- stap_out\nrewrite: cookies: 0\ncontent: cookies: 1\n\n--- response_body\nCookie foo: \nCookie baz: \nCookie boo: 123\nCookie: boo=123\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: set multiple custom cookies (with existing cookies)\n--- config\n    location /t {\n        rewrite_by_lua '\n           ngx.req.set_header(\"Cookie\", {\"boo=123\",\"foo=78\"})\n        ';\n        echo \"Cookie foo: $cookie_foo\";\n        echo \"Cookie baz: $cookie_baz\";\n        echo \"Cookie boo: $cookie_boo\";\n        echo \"Cookie: $http_cookie\";\n    }\n--- request\nGET /t\n--- more_headers\nCookie: foo=bar\nCookie: baz=blah\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: cookies: %d\\n\", $r->headers_in->cookies->nelts)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: cookies: %d\\n\", $r->headers_in->cookies->nelts)\n}\n\n--- stap_out\nrewrite: cookies: 2\ncontent: cookies: 2\n\n--- response_body\nCookie foo: 78\nCookie baz: \nCookie boo: 123\nCookie: boo=123; foo=78\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: set multiple custom cookies (without existing cookies)\n--- config\n    location /t {\n        rewrite_by_lua '\n           ngx.req.set_header(\"Cookie\", {\"boo=123\", \"foo=bar\"})\n        ';\n        echo \"Cookie foo: $cookie_foo\";\n        echo \"Cookie baz: $cookie_baz\";\n        echo \"Cookie boo: $cookie_boo\";\n        echo \"Cookie: $http_cookie\";\n    }\n--- request\nGET /t\n\n--- stap\nF(ngx_http_lua_rewrite_by_chunk) {\n    printf(\"rewrite: cookies: %d\\n\", $r->headers_in->cookies->nelts)\n}\n\nF(ngx_http_core_content_phase) {\n    printf(\"content: cookies: %d\\n\", $r->headers_in->cookies->nelts)\n}\n\n--- stap_out\nrewrite: cookies: 0\ncontent: cookies: 2\n\n--- response_body\nCookie foo: bar\nCookie baz: \nCookie boo: 123\nCookie: boo=123; foo=bar\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: set multiple custom cookies with unsafe values (with '\\n' and 'r')\n--- config\n    location /t {\n        rewrite_by_lua_block {\n           ngx.req.set_header(\"Cookie\", {\"boo=123\\nfoo\", \"foo=bar\\rbar\"})\n        }\n        echo \"Cookie foo: $cookie_foo\";\n        echo \"Cookie baz: $cookie_baz\";\n        echo \"Cookie boo: $cookie_boo\";\n        echo \"Cookie: $http_cookie\";\n    }\n--- request\nGET /t\n--- response_body\nCookie foo: bar%0Dbar\nCookie baz: \nCookie boo: 123%0Afoo\nCookie: boo=123%0Afoo; foo=bar%0Dbar\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/114-config.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: ngx.config.debug\n--- config\n    location /t {\n        content_by_lua '\n            ngx.say(\"debug: \", ngx.config.debug)\n        ';\n    }\n--- request\nGET /t\n--- response_body_like chop\n^debug: (?:true|false)$\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: ngx.config.subsystem\n--- config\n    location /t {\n        content_by_lua '\n            ngx.say(\"subsystem: \", ngx.config.subsystem)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nsubsystem: http\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/115-quote-sql-str.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#log_level(\"warn\");\nno_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: \\0\n--- config\n    location = /set {\n        content_by_lua '\n            ngx.say(ngx.quote_sql_str(\"a\\\\0b\\\\0\"))\n        ';\n    }\n--- request\nGET /set\n--- response_body\n'a\\0b\\0'\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: \\t\n--- config\n    location = /set {\n        content_by_lua '\n            ngx.say(ngx.quote_sql_str(\"a\\\\tb\\\\t\"))\n        ';\n    }\n--- request\nGET /set\n--- response_body\n'a\\tb\\t'\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: \\b\n--- config\n    location = /set {\n        content_by_lua '\n            ngx.say(ngx.quote_sql_str(\"a\\\\bb\\\\b\"))\n        ';\n    }\n--- request\nGET /set\n--- response_body\n'a\\bb\\b'\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: \\Z\n--- config\n    location = /set {\n        content_by_lua '\n            ngx.say(ngx.quote_sql_str(\"a\\\\026b\\\\026\"))\n        ';\n    }\n--- request\nGET /set\n--- response_body\n'a\\Zb\\Z'\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/116-raw-req-socket.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_USE_HTTP3}) {\n        $SkipReason = \"http3 does not support ngx.req.socket(true)\";\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 46;\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\n#log_level 'warn';\nlog_level 'debug';\n\n#no_long_string();\n#no_diff();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    server_tokens off;\n    location = /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local req = \"GET /mysock HTTP/1.1\\\\r\\\\nUpgrade: mysock\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\nhello\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local data, err, partial = reader()\n            if not data then\n                ngx.say(\"no response header found\")\n                return\n            end\n\n            local msg, err = sock:receive()\n            if not msg then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"msg: \", msg)\n\n            ok, err = sock:close()\n            if not ok then\n                ngx.say(\"failed to close socket: \", err)\n                return\n            end\n        ';\n    }\n\n    location = /mysock {\n        content_by_lua '\n            ngx.status = 101\n            ngx.send_headers()\n            ngx.flush(true)\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n\n            local data, err = sock:receive(5)\n            if not data then\n                ngx.log(ngx.ERR, \"server: failed to receive: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"1: received: \" .. data .. \"\\\\n\")\n            if not bytes then\n                ngx.log(ngx.ERR, \"server: failed to send: \", err)\n                return\n            end\n        ';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\nmsg: 1: received: hello\n--- grep_error_log: lua socket tcp_nodelay\n--- grep_error_log_out\nlua socket tcp_nodelay\nlua socket tcp_nodelay\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: header not sent yet\n--- config\n    server_tokens off;\n    location = /t {\n        content_by_lua '\n            ngx.status = 101\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n            local ok, err = sock:send(\"HTTP/1.1 200 OK\\\\r\\\\nContent-Length: 5\\\\r\\\\n\\\\r\\\\nhello\")\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to send: \", err)\n                return\n            end\n        ';\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.0\\r\nHost: localhost\\r\nContent-Length: 5\\r\n\\r\nhello\"\n--- response_headers\nContent-Length: 5\n--- response_body chop\nhello\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: http 1.0 buffering\n--- config\n    server_tokens off;\n    location = /t {\n        content_by_lua '\n            ngx.say(\"hello\")\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return ngx.exit(500)\n            end\n        ';\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.0\\r\nHost: localhost\\r\nUpgrade: mysocket\\r\n\\r\nhello\"\n--- stap2\nF(ngx_http_header_filter) {\n    println(\"header filter\")\n}\nF(ngx_http_lua_req_socket) {\n    println(\"lua req socket\")\n}\n--- ignore_response\n--- error_log\nserver: failed to get raw req socket: http 1.0 buffering\n\n\n\n=== TEST 4: multiple raw req sockets\n--- config\n    server_tokens off;\n    location = /t {\n        content_by_lua '\n            ngx.say(\"hello\")\n            ngx.flush(true)\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n            local sock2, err = ngx.req.socket(true)\n            if not sock2 then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket2: \", err)\n                return\n            end\n\n        ';\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nUpgrade: mysocket\\r\n\\r\nhello\"\n--- stap2\nF(ngx_http_header_filter) {\n    println(\"header filter\")\n}\nF(ngx_http_lua_req_socket) {\n    println(\"lua req socket\")\n}\n--- ignore_response\n--- error_log\nserver: failed to get raw req socket2: duplicate call\n\n\n\n=== TEST 5: ngx.say after ngx.req.socket(true)\n--- config\n    server_tokens off;\n    location = /t {\n        content_by_lua '\n            ngx.send_headers()\n            ngx.flush(true)\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n            local ok, err = ngx.say(\"ok\")\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to say: \", err)\n                return\n            end\n        ';\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nUpgrade: mysocket\\r\n\\r\nhello\"\n--- ignore_response\n--- error_log\nfailed to say: raw request socket acquired\n\n\n\n=== TEST 6: ngx.print after ngx.req.socket(true)\n--- config\n    server_tokens off;\n    location = /t {\n        content_by_lua '\n            ngx.send_headers()\n            ngx.flush(true)\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n            local ok, err = ngx.print(\"ok\")\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to print: \", err)\n                return\n            end\n        ';\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nUpgrade: mysocket\\r\n\\r\nhello\"\n--- ignore_response\n--- error_log\nfailed to print: raw request socket acquired\n\n\n\n=== TEST 7: ngx.eof after ngx.req.socket(true)\n--- config\n    server_tokens off;\n    location = /t {\n        content_by_lua '\n            ngx.send_headers()\n            ngx.flush(true)\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n            local ok, err = ngx.eof()\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to eof: \", err)\n                return\n            end\n        ';\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nUpgrade: mysocket\\r\n\\r\nhello\"\n--- ignore_response\n--- error_log\nfailed to eof: raw request socket acquired\n\n\n\n=== TEST 8: ngx.flush after ngx.req.socket(true)\n--- config\n    server_tokens off;\n    location = /t {\n        content_by_lua '\n            ngx.send_headers()\n            ngx.flush(true)\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n            local ok, err = ngx.flush()\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to flush: \", err)\n                return\n            end\n        ';\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nUpgrade: mysocket\\r\n\\r\nhello\"\n--- ignore_response\n--- error_log\nfailed to flush: raw request socket acquired\n\n\n\n=== TEST 9: receive timeout\n--- config\n    server_tokens off;\n    postpone_output 1;\n    location = /t {\n        content_by_lua '\n            ngx.send_headers()\n            ngx.req.read_body()\n            ngx.flush(true)\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n\n            sock:settimeout(100)\n\n            local data, err, partial = sock:receive(10)\n            if not data then\n                ngx.log(ngx.ERR, \"server: 1: failed to receive: \", err, \", received: \", partial)\n            end\n\n            data, err, partial = sock:receive(10)\n            if not data then\n                ngx.log(ngx.ERR, \"server: 2: failed to receive: \", err, \", received: \", partial)\n            end\n\n            ngx.exit(444)\n        ';\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nUpgrade: mysocket\\r\nConnection: close\\r\n\\r\nab\"\n--- ignore_response\n--- wait: 0.1\n--- error_log\nlua tcp socket read timed out\nserver: 1: failed to receive: timeout, received: ab,\nserver: 2: failed to receive: timeout, received: ,\n--- no_error_log\n[alert]\n\n\n\n=== TEST 10: on_abort called during ngx.sleep()\n--- config\n    server_tokens off;\n    lua_check_client_abort on;\n    location = /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local req = \"GET /mysock HTTP/1.1\\\\r\\\\nUpgrade: mysock\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\nhello\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local data, err, partial = reader()\n            if not data then\n                ngx.say(\"no response header found\")\n                return\n            end\n\n            local msg, err = sock:receive()\n            if not msg then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"msg: \", msg)\n\n            ngx.sleep(0.1)\n\n            ok, err = sock:close()\n            if not ok then\n                ngx.say(\"failed to close socket: \", err)\n                return\n            end\n        ';\n    }\n\n    location = /mysock {\n        content_by_lua '\n            ngx.status = 101\n            ngx.send_headers()\n            ngx.flush(true)\n\n            local ok, err = ngx.on_abort(function (premature) ngx.log(ngx.WARN, \"mysock handler aborted\") end)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set on_abort handler: \", err)\n                return\n            end\n\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n\n            local data, err = sock:receive(5)\n            if not data then\n                ngx.log(ngx.ERR, \"server: failed to receive: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"1: received: \" .. data .. \"\\\\n\")\n            if not bytes then\n                ngx.log(ngx.ERR, \"server: failed to send: \", err)\n                return\n            end\n\n            ngx.sleep(1)\n        ';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\nmsg: 1: received: hello\n--- error_log\nmysock handler aborted\n--- no_error_log\n[error]\n--- wait: 1.1\n\n\n\n=== TEST 11: on_abort called during sock:receive()\n--- config\n    server_tokens off;\n    lua_check_client_abort on;\n    location = /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local req = \"GET /mysock HTTP/1.1\\\\r\\\\nUpgrade: mysock\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\nhello\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local data, err, partial = reader()\n            if not data then\n                ngx.say(\"no response header found\")\n                return\n            end\n\n            local msg, err = sock:receive()\n            if not msg then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"msg: \", msg)\n\n            ngx.sleep(0.1)\n\n            ok, err = sock:close()\n            if not ok then\n                ngx.say(\"failed to close socket: \", err)\n                return\n            end\n        ';\n    }\n\n    location = /mysock {\n        content_by_lua '\n            ngx.status = 101\n            ngx.send_headers()\n            ngx.flush(true)\n\n            local ok, err = ngx.on_abort(function (premature) ngx.log(ngx.WARN, \"mysock handler aborted\") end)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set on_abort handler: \", err)\n                return\n            end\n\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n\n            local data, err = sock:receive(5)\n            if not data then\n                ngx.log(ngx.ERR, \"server: failed to receive: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"1: received: \" .. data .. \"\\\\n\")\n            if not bytes then\n                ngx.log(ngx.ERR, \"server: failed to send: \", err)\n                return\n            end\n\n            local data, err = sock:receive()\n            if not data then\n                ngx.log(ngx.WARN, \"failed to receive a line: \", err)\n                return\n            end\n        ';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\nmsg: 1: received: hello\n--- error_log\nfailed to receive a line: client aborted\n--- no_error_log\n[error]\n--- wait: 0.1\n\n\n\n=== TEST 12: receiveuntil\n--- config\n    server_tokens off;\n    location = /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local req = \"GET /mysock HTTP/1.1\\\\r\\\\nUpgrade: mysock\\\\r\\\\nHost: localhost\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\nhello\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\", \")\n            if not bytes then\n                ngx.say(\"failed to send packet 1: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"world\")\n            if not bytes then\n                ngx.say(\"failed to send packet 2: \", err)\n                return\n            end\n\n            local reader = sock:receiveuntil(\"\\\\r\\\\n\\\\r\\\\n\")\n            local data, err, partial = reader()\n            if not data then\n                ngx.say(\"no response header found\")\n                return\n            end\n\n            local msg, err = sock:receive()\n            if not msg then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"msg: \", msg)\n\n            ok, err = sock:close()\n            if not ok then\n                ngx.say(\"failed to close socket: \", err)\n                return\n            end\n        ';\n    }\n\n    location = /mysock {\n        content_by_lua '\n            ngx.status = 101\n            ngx.send_headers()\n            ngx.flush(true)\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n\n            local reader = sock:receiveuntil(\"rld\")\n            local data, err = reader()\n            if not data then\n                ngx.log(ngx.ERR, \"server: failed to receive: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"1: received: \" .. data .. \"\\\\n\")\n            if not bytes then\n                ngx.log(ngx.ERR, \"server: failed to send: \", err)\n                return\n            end\n        ';\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\nmsg: 1: received: hello, wo\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: request body not read yet\n--- config\n    server_tokens off;\n    location = /t {\n        content_by_lua '\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n\n            local data, err = sock:receive(5)\n            if not data then\n                ngx.log(ngx.ERR, \"failed to receive: \", err)\n                return\n            end\n\n            local ok, err = sock:send(\"HTTP/1.1 200 OK\\\\r\\\\nContent-Length: 5\\\\r\\\\n\\\\r\\\\n\" .. data)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to send: \", err)\n                return\n            end\n        ';\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.0\\r\nHost: localhost\\r\nContent-Length: 5\\r\n\\r\nhello\"\n--- response_headers\nContent-Length: 5\n--- response_body chop\nhello\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: pending request body reading\n--- config\n    server_tokens off;\n    location = /t {\n        content_by_lua '\n            ngx.thread.spawn(function ()\n                ngx.req.read_body()\n            end)\n\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.WARN, \"server: failed to get raw req socket: \", err)\n                return ngx.exit(444)\n            end\n\n            local data, err = sock:receive(5)\n            if not data then\n                ngx.log(ngx.ERR, \"failed to receive: \", err)\n                return\n            end\n\n            local ok, err = sock:send(\"HTTP/1.1 200 OK\\\\r\\\\nContent-Length: 5\\\\r\\\\n\\\\r\\\\n\" .. data)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to send: \", err)\n                return\n            end\n        ';\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.0\\r\nHost: localhost\\r\nContent-Length: 5\\r\n\\r\nhell\"\n--- ignore_response\n--- no_error_log\n[error]\n[alert]\n--- error_log\nserver: failed to get raw req socket: pending request body reading in some other thread\n\n\n\n=== TEST 15: read chunked request body with raw req socket\n--- config\n    location = /t {\n        content_by_lua '\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"failed to new: \", err)\n                return\n            end\n            local function err(...)\n                ngx.log(ngx.ERR, ...)\n                return ngx.exit(400)\n            end\n            local num = tonumber\n            local MAX_CHUNKS = 1000\n            local eof = false\n            local chunks = {}\n            for i = 1, MAX_CHUNKS do\n                local line, err = sock:receive()\n                if not line then\n                    err(\"failed to receive chunk size: \", err)\n                end\n\n                local size = num(line, 16)\n                if not size then\n                    err(\"bad chunk size: \", line)\n                end\n\n                if size == 0 then -- last chunk\n                    -- receive the last line\n                    line, err = sock:receive()\n                    if not line then\n                        err(\"failed to receive last chunk: \", err)\n                    end\n\n                    if line ~= \"\" then\n                        err(\"bad last chunk: \", line)\n                    end\n\n                    eof = true\n                    break\n                end\n\n                local chunk, err = sock:receive(size)\n                if not chunk then\n                    err(\"failed to receive chunk of size \", size, \": \", err)\n                end\n\n                local data, err = sock:receive(2)\n                if not data then\n                    err(\"failed to receive chunk terminator: \", err)\n                end\n\n                if data ~= \"\\\\r\\\\n\" then\n                    err(\"bad chunk terminator: \", data)\n                end\n\n                chunks[i] = chunk\n            end\n\n            if not eof then\n                err(\"too many chunks (more than \", MAX_CHUNKS, \")\")\n            end\n\n            local concat = table.concat\n            local body = concat{\"got \", #chunks, \" chunks.\\\\nrequest body: \"}\n                         .. concat(chunks) .. \"\\\\n\"\n            local ok, err = sock:send(\"HTTP/1.1 200 OK\\\\r\\\\nConnection: close\\\\r\\\\nContent-Length: \"\n                            .. #body .. \"\\\\r\\\\n\\\\r\\\\n\" .. body)\n            if not ok then\n                err(\"failed to send response: \", err)\n            end\n        ';\n    }\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nTransfer-Encoding: chunked\\r\nConnection: close\\r\n\\r\n5\\r\nhey, \\r\nb\\r\nhello world\\r\n0\\r\n\\r\n\"\n--- response_body\ngot 2 chunks.\nrequest body: hey, hello world\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 16: receiveany\n--- config\n    server_tokens off;\n    location = /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local req = \"GET /mysock HTTP/1.1\\r\\nUpgrade: mysock\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\nhello\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            -- Will return to I/O loop, causing receiveany() in /mysock location to be called\n            ngx.sleep(1)\n\n            local bytes, err = sock:send(\", world\")\n            if not bytes then\n                ngx.say(\"failed to send packet 1: \", err)\n                return\n            end\n\n            local reader = sock:receiveuntil(\"\\r\\n\\r\\n\")\n            local data, err, partial = reader()\n            if not data then\n                ngx.say(\"no response header found\")\n                return\n            end\n\n            local msg, err = sock:receive()\n            if not msg then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"msg: \", msg)\n\n            ok, err = sock:close()\n            if not ok then\n                ngx.say(\"failed to close socket: \", err)\n                return\n            end\n        }\n    }\n\n    location = /mysock {\n        content_by_lua_block {\n            ngx.status = 101\n            ngx.send_headers()\n            ngx.flush(true)\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n\n            local data, err = sock:receiveany(1024)\n            if not data then\n                ngx.log(ngx.ERR, \"server: failed to receive: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"1: received: \" .. data .. \"\\n\")\n            if not bytes then\n                ngx.log(ngx.ERR, \"server: failed to send: \", err)\n                return\n            end\n        }\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\nmsg: 1: received: hello\n--- no_error_log\n[error]\n--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne \"\")\n\n\n\n=== TEST 17: getfd()\n--- config\n    server_tokens off;\n    location = /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local req = \"GET /mysock HTTP/1.1\\r\\nUpgrade: mysock\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\nhello\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            local reader = sock:receiveuntil(\"\\r\\n\\r\\n\")\n            local data, err, partial = reader()\n            if not data then\n                ngx.say(\"no response header found\")\n                return\n            end\n\n            local msg, err = sock:receive()\n            if not msg then\n                ngx.say(\"failed to receive: \", err)\n                return\n            end\n\n            ngx.say(\"msg: \", msg)\n\n            ok, err = sock:close()\n            if not ok then\n                ngx.say(\"failed to close socket: \", err)\n                return\n            end\n        }\n    }\n\n    location = /mysock {\n        content_by_lua_block {\n            ngx.status = 101\n            ngx.send_headers()\n            ngx.flush(true)\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n\n            local s = sock:getfd()\n            local data, err = sock:receive(5)\n            if not data then\n                ngx.log(ngx.ERR, \"server: failed to receive: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"fd: \" .. tostring(s) .. \" 1: received: \" .. data .. \"\\n\")\n            if not bytes then\n                ngx.log(ngx.ERR, \"server: failed to send: \", err)\n                return\n            end\n        }\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body eval\nqr/\\Amsg: fd: \\d+ 1: received: hello\n/ms\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/117-raw-req-socket-timeout.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nBEGIN {\n    if (!defined $ENV{LD_PRELOAD}) {\n        $ENV{LD_PRELOAD} = '';\n    }\n\n    if ($ENV{LD_PRELOAD} !~ /\\bmockeagain\\.so\\b/) {\n        $ENV{LD_PRELOAD} = \"mockeagain.so $ENV{LD_PRELOAD}\";\n    }\n\n    if ($ENV{MOCKEAGAIN} eq 'r') {\n        $ENV{MOCKEAGAIN} = 'rw';\n\n    } else {\n        $ENV{MOCKEAGAIN} = 'w';\n    }\n\n    $ENV{TEST_NGINX_EVENT_TYPE} = 'poll';\n    $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'hello, world';\n    $ENV{TEST_NGINX_POSTPONE_OUTPUT} = 1;\n}\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: pending response header data\n--- config\n    server_tokens off;\n    postpone_output 1;\n    location = /t {\n        content_by_lua '\n            ngx.send_headers()\n            ngx.req.read_body()\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n        ';\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nUpgrade: mysocket\\r\nConnection: close\\r\n\\r\n\"\n--- stap2\nF(ngx_http_header_filter) {\n    println(\"header filter\")\n}\nF(ngx_http_lua_req_socket) {\n    println(\"lua req socket\")\n}\n--- response_body\n--- error_log\nserver: failed to get raw req socket: pending data to write\n\n\n\n=== TEST 2: send timeout\n--- config\n    server_tokens off;\n    postpone_output 1;\n    location = /t {\n        content_by_lua '\n            ngx.send_headers()\n            ngx.req.read_body()\n            ngx.flush(true)\n            local sock, err = ngx.req.socket(true)\n            if not sock then\n                ngx.log(ngx.ERR, \"server: failed to get raw req socket: \", err)\n                return\n            end\n            sock:settimeout(100)\n            local ok, err = sock:send(\"hello, world!\")\n            if not ok then\n                ngx.log(ngx.ERR, \"server: failed to send: \", err)\n            end\n            ngx.exit(444)\n        ';\n    }\n\n--- raw_request eval\n\"GET /t HTTP/1.1\\r\nHost: localhost\\r\nUpgrade: mysocket\\r\nConnection: close\\r\n\\r\n\"\n--- ignore_response\n--- error_log\nlua tcp socket write timed out\nserver: failed to send: timeout\n--- no_error_log\n[alert]\n"
  },
  {
    "path": "t/118-use-default-type.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4);\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\n#log_level 'warn';\nlog_level 'debug';\n\n#no_long_string();\n#no_diff();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: lua_use_default_type default on\n--- config\n    location /lua {\n        default_type text/plain;\n        content_by_lua '\n            ngx.say(\"hello\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nhello\n--- response_headers\nContent-Type: text/plain\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: lua_use_default_type explicitly on\n--- config\n    lua_use_default_type on;\n    location /lua {\n        default_type text/plain;\n        content_by_lua '\n            ngx.say(\"hello\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nhello\n--- response_headers\nContent-Type: text/plain\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: lua_use_default_type off\n--- config\n    lua_use_default_type off;\n    location /lua {\n        default_type text/plain;\n        content_by_lua '\n            ngx.say(\"hello\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nhello\n--- response_headers\n!Content-Type\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: overriding lua_use_default_type off\n--- config\n    lua_use_default_type off;\n    location /lua {\n        lua_use_default_type on;\n        default_type text/plain;\n        content_by_lua '\n            ngx.say(\"hello\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nhello\n--- response_headers\nContent-Type: text/plain\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: overriding lua_use_default_type on\n--- config\n    lua_use_default_type on;\n    location /lua {\n        lua_use_default_type off;\n        default_type text/plain;\n        content_by_lua '\n            ngx.say(\"hello\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nhello\n--- response_headers\n!Content-Type\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: lua_use_default_type on does not set content type on 304\n--- config\n    lua_use_default_type on;\n    location /lua {\n        default_type text/plain;\n        content_by_lua '\n            ngx.status = ngx.HTTP_NOT_MODIFIED\n        ';\n    }\n--- request\nGET /lua\n--- response_body\n--- response_headers\n!Content-Type\n--- no_error_log\n[error]\n--- error_code: 304\n"
  },
  {
    "path": "t/119-config-prefix.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: content_by_lua\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(\"prefix: \", ngx.config.prefix())\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like chop\n^prefix: \\/\\S+$\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/120-re-find.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 1);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, 1234\"\n            local from, to, err = ngx.re.find(s, \"([0-9]+)\", \"jo\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                end\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 8\nto: 11\nmatched: 1234\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: empty matched string\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, world\"\n            local from, to, err = ngx.re.find(s, \"[0-9]*\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                end\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 1\nto: 0\nmatched: \n--- no_error_log\n[error]\n\n\n\n=== TEST 3: multiple captures (with o)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, 1234\"\n            local from, to, err = ngx.re.find(s, \"([a-z]+).*?([0-9]{2})[0-9]+\", \"o\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n            else\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 1\nto: 11\nmatched: hello, 1234\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: not matched\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, 1234\"\n            local from, to, err = ngx.re.find(s, \"foo\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n\n            else\n                ngx.say(\"not matched.\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched.\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: case sensitive by default\n--- config\n    location /re {\n        content_by_lua '\n            local from = ngx.re.find(\"hello, 1234\", \"HELLO\")\n            if from then\n                ngx.say(from)\n            else\n                ngx.say(\"not matched.\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched.\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: case insensitive\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, 1234\"\n            local from, to, err = ngx.re.find(s, \"HELLO\", \"i\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n\n            else\n                ngx.say(\"not matched.\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 1\nto: 5\nmatched: hello\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: UTF-8 mode\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello章亦春\"\n            local from, to, err = ngx.re.find(s, \"HELLO.{2}\", \"iu\")\n            if not from then\n                ngx.say(\"FAIL: \", err)\n                return\n            end\n\n            ngx.say(string.sub(s, from, to))\n        ';\n    }\n--- request\n    GET /re\n--- response_body_like chop\n^(?:FAIL: bad argument \\#2 to '\\?' \\(pcre_compile\\(\\) failed: this version of PCRE is not compiled with PCRE_UTF8 support in \"HELLO\\.\\{2\\}\" at \"HELLO\\.\\{2\\}\"\\)|hello章亦)$\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: multi-line mode (^ at line head)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello\\\\nworld\"\n            local from, to, err = ngx.re.find(s, \"^world\", \"m\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n\n            else\n                ngx.say(\"not matched.\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 7\nto: 11\nmatched: world\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: multi-line mode (. does not match \\n)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello\\\\nworld\"\n            local from, to, err = ngx.re.find(s, \".*\", \"m\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n\n            else\n                ngx.say(\"not matched.\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 1\nto: 5\nmatched: hello\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: single-line mode (^ as normal)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello\\\\nworld\"\n            local from, to, err = ngx.re.find(s, \"^world\", \"s\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n\n            else\n                ngx.say(\"not matched.\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched.\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: single-line mode (dot all)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello\\\\nworld\"\n            local from, to, err = ngx.re.find(s, \".*\", \"s\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n\n            else\n                ngx.say(\"not matched.\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 1\nto: 11\nmatched: hello\nworld\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: extended mode (ignore whitespaces)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello\\\\nworld\"\n            local from, to, err = ngx.re.find(s, \"\\\\\\\\w     \\\\\\\\w\", \"x\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n\n            else\n                ngx.say(\"not matched.\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 1\nto: 2\nmatched: he\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: bad pattern\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello\\\\nworld\"\n            local from, to, err = ngx.re.find(s, \"(abc\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n\n                else\n                    ngx.say(\"not matched.\")\n                end\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre2_compile() failed: missing closing parenthesis in \\\"(abc\\\"\\n\"\n:\n\"error: pcre_compile() failed: missing ) in \\\"(abc\\\"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: bad option\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello\\\\nworld\"\n            local from, to, err = ngx.re.find(s, \".*\", \"H\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                    return\n                end\n\n                ngx.say(\"not matched.\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log\nunknown flag \"H\"\n\n\n\n=== TEST 15: anchored match (failed)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, 1234\"\n            local from, to, err = ngx.re.find(s, \"([0-9]+)\", \"a\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                    return\n                end\n\n                ngx.say(\"not matched.\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched.\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: anchored match (succeeded)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"1234, hello\"\n            local from, to, err = ngx.re.find(s, \"([0-9]+)\", \"a\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                    return\n                end\n\n                ngx.say(\"not matched.\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 1\nto: 4\nmatched: 1234\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: match with ctx but no pos\n--- config\n    location /re {\n        content_by_lua '\n            local ctx = {}\n            local from, to = ngx.re.find(\"1234, hello\", \"([0-9]+)\", \"\", ctx)\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"pos: \", ctx.pos)\n            else\n                ngx.say(\"not matched!\")\n                ngx.say(\"pos: \", ctx.pos)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 1\nto: 4\npos: 5\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: match with ctx and a pos\n--- config\n    location /re {\n        content_by_lua '\n            local ctx = { pos = 3 }\n            local from, to, err = ngx.re.find(\"1234, hello\", \"([0-9]+)\", \"\", ctx)\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"pos: \", ctx.pos)\n            else\n                ngx.say(\"not matched!\")\n                ngx.say(\"pos: \", ctx.pos)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 3\nto: 4\npos: 5\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: named subpatterns w/ extraction\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, 1234\"\n            local from, to, err = ngx.re.find(s, \"(?<first>[a-z]+), [0-9]+\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                    return\n                end\n\n                ngx.say(\"not matched.\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 1\nto: 11\nmatched: hello, 1234\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: bad UTF-8\n--- config\n    location = /t {\n        content_by_lua '\n            local target = \"你好\"\n            local regex = \"你好\"\n\n            local from, to, err = ngx.re.find(string.sub(target, 1, 4), regex, \"u\")\n\n            if err then\n                ngx.say(\"error: \", err)\n                return\n            end\n\n            if m then\n                ngx.say(\"matched: \", from)\n            else\n                ngx.say(\"not matched\")\n            end\n        ';\n    }\n--- request\nGET /t\n--- response_body eval\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"error: pcre_exec\\(\\) failed: -4\\n\"\n:\n\"error: pcre_exec\\(\\) failed: -10\\n\"\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: UTF-8 mode without UTF-8 sequence checks\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"你好\"\n            local from, to, err = ngx.re.find(s, \".\", \"U\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n\n            else\n                ngx.say(\"not matched.\")\n            end\n        ';\n    }\n--- stap\n# TODO: PCRE2 use different option values from PCRE\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_compile\") {\n    printf(\"compile opts: %x\\n\", $options)\n}\n\nprobe process(\"$LIBPCRE_PATH\").function(\"pcre_exec\") {\n    printf(\"exec opts: %x\\n\", $options)\n}\n\n--- stap_out\ncompile opts: 800\nexec opts: 2000\n\n--- request\n    GET /re\n--- response_body\nfrom: 1\nto: 3\nmatched: 你\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: just hit match limit\n--- http_config\n    lua_regex_match_limit 5000;\n--- config\n    location /re {\n        content_by_lua_file html/a.lua;\n    }\n\n--- user_files\n>>> a.lua\nlocal re = [==[(?i:([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:=|<=>|r?like|sounds\\s+like|regexp)([\\s'\\\"`´’‘\\(\\)]*)?\\2|([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:!=|<=|>=|<>|<|>|\\^|is\\s+not|not\\s+like|not\\s+regexp)([\\s'\\\"`´’‘\\(\\)]*)?(?!\\6)([\\d\\w]+))]==]\n\nlocal s = string.rep([[ABCDEFG]], 10)\n\nlocal start = ngx.now()\n\nlocal from, to, err = ngx.re.find(s, re, \"o\")\n\n--[[\nngx.update_time()\nlocal elapsed = ngx.now() - start\nngx.say(elapsed, \" sec elapsed.\")\n]]\n\nif not from then\n    if err then\n        ngx.say(\"error: \", err)\n        return\n    end\n    ngx.say(\"failed to match.\")\n    return\nend\n\n--- request\n    GET /re\n--- response_body eval\n# lua_regex_match_limit uses pcre_extra->match_limit in the PCRE,\n# but PCRE2 replaces this with pcre2_set_match_limit interface,\n# which has different effects.\n$Test::Nginx::Util::PcreVersion == 2 ?\n\"failed to match.\\n\"\n:\n\"error: pcre_exec() failed: -8\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: just not hit match limit\n--- http_config\n    lua_regex_match_limit 5100;\n--- config\n    location /re {\n        content_by_lua_file html/a.lua;\n    }\n\n--- user_files\n>>> a.lua\nlocal re = [==[(?i:([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:=|<=>|r?like|sounds\\s+like|regexp)([\\s'\\\"`´’‘\\(\\)]*)?\\2|([\\s'\\\"`´’‘\\(\\)]*)?([\\d\\w]+)([\\s'\\\"`´’‘\\(\\)]*)?(?:!=|<=|>=|<>|<|>|\\^|is\\s+not|not\\s+like|not\\s+regexp)([\\s'\\\"`´’‘\\(\\)]*)?(?!\\6)([\\d\\w]+))]==]\n\nlocal s = string.rep([[ABCDEFG]], 10)\n\nlocal start = ngx.now()\n\nlocal from, to, err = ngx.re.find(s, re, \"o\")\n\n--[[\nngx.update_time()\nlocal elapsed = ngx.now() - start\nngx.say(elapsed, \" sec elapsed.\")\n]]\n\nif not from then\n    if err then\n        ngx.say(\"error: \", err)\n        return\n    end\n    ngx.say(\"failed to match\")\n    return\nend\n\n--- request\n    GET /re\n--- response_body\nfailed to match\n--- no_error_log\n[error]\n\n\n\n=== TEST 24: specify the group (1)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, 1234\"\n            local from, to, err = ngx.re.find(s, \"([0-9])([0-9]+)\", \"jo\", nil, 1)\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                end\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 8\nto: 8\nmatched: 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 25: specify the group (0)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, 1234\"\n            local from, to, err = ngx.re.find(s, \"([0-9])([0-9]+)\", \"jo\", nil, 0)\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                end\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 8\nto: 11\nmatched: 1234\n--- no_error_log\n[error]\n\n\n\n=== TEST 26: specify the group (2)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, 1234\"\n            local from, to, err = ngx.re.find(s, \"([0-9])([0-9]+)\", \"jo\", nil, 2)\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                end\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 9\nto: 11\nmatched: 234\n--- no_error_log\n[error]\n\n\n\n=== TEST 27: specify the group (3)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, 1234\"\n            local from, to, err = ngx.re.find(s, \"([0-9])([0-9]+)\", \"jo\", nil, 3)\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                    return\n                end\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: nth out of bound\n--- no_error_log\n[error]\n\n\n\n=== TEST 28: specify the group (4)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, 1234\"\n            local from, to, err = ngx.re.find(s, \"([0-9])([0-9]+)\", \"jo\", nil, 4)\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                    return\n                end\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nerror: nth out of bound\n--- no_error_log\n[error]\n\n\n\n=== TEST 29: nil submatch (2nd)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, 1234\"\n            local from, to, err = ngx.re.find(s, \"([0-9])|(hello world)\", \"jo\", nil, 2)\n            if from or to then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                    return\n                end\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched!\n--- no_error_log\n[error]\n\n\n\n=== TEST 30: nil submatch (1st)\n--- config\n    location /re {\n        content_by_lua '\n            local s = \"hello, 1234\"\n            local from, to, err = ngx.re.find(s, \"(hello world)|([0-9])\", \"jo\", nil, 1)\n            if from or to then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                    return\n                end\n                ngx.say(\"not matched!\")\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nnot matched!\n--- no_error_log\n[error]\n\n\n\n=== TEST 31: match with ctx and a pos (anchored by \\G)\n--- config\n    location /re {\n        content_by_lua '\n            local ctx = { pos = 3 }\n            local from, to, err = ngx.re.find(\"1234, hello\", [[(\\G[0-9]+)]], \"\", ctx)\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"pos: \", ctx.pos)\n            else\n                ngx.say(\"not matched!\")\n                ngx.say(\"pos: \", ctx.pos)\n            end\n        ';\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 3\nto: 4\npos: 5\n--- no_error_log\n[error]\n\n\n\n=== TEST 32: ignore match limit in DFA mode\n--- http_config\n    lua_regex_match_limit 1;\n--- config\n    location /re {\n        content_by_lua_block {\n            local s = \"This is <something> <something else> <something further> no more\"\n            local from, to, err = ngx.re.find(s, \"<.*>\", \"d\")\n            if from then\n                ngx.say(\"from: \", from)\n                ngx.say(\"to: \", to)\n                ngx.say(\"matched: \", string.sub(s, from, to))\n            else\n                if err then\n                    ngx.say(\"error: \", err)\n                    return\n                end\n                ngx.say(\"not matched!\")\n            end\n        }\n    }\n--- request\n    GET /re\n--- response_body\nfrom: 9\nto: 56\nmatched: <something> <something else> <something further>\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/121-version.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: nginx version\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(\"version: \", ngx.config.nginx_version)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like chop\n^version: \\d+$\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: ngx_lua_version\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(\"version: \", ngx.config.ngx_lua_version)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like chop\n^version: \\d+$\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/122-worker-2.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\nmaster_on();\nworkers(4);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: get worker pids with multiple worker\n--- config\n    location /lua {\n        content_by_lua_block {\n            local pids, err = ngx.worker.pids()\n            if err ~= nil then\n                return\n            end\n            local pid = ngx.worker.pid()\n            ngx.say(\"worker pid: \", pid)\n            local count = ngx.worker.count()\n            ngx.say(\"worker count: \", count)\n            ngx.say(\"worker pids count: \", #pids)\n            for i = 1, count do\n                if pids[i] == pid then\n                    ngx.say(\"worker pid is correct.\")\n                    return\n                end\n            end\n        }\n    }\n--- request\nGET /lua\n--- response_body_like\nworker pid: \\d+\nworker count: 4\nworker pids count: 4\nworker pid is correct\\.\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/122-worker-3.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_CHECK_LEAK}) {\n        $SkipReason = \"unavailable for the hup tests\";\n\n    } else {\n        $ENV{TEST_NGINX_USE_HUP} = 1;\n        undef $ENV{TEST_NGINX_USE_STAP};\n    }\n}\n\nuse Test::Nginx::Socket::Lua 'no_plan';\n\n#worker_connections(1014);\nmaster_on();\nworkers(4);\n#log_level('warn');\n\nrepeat_each(2);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: get worker pids with multiple worker\n--- config\n    location /lua {\n        content_by_lua_block {\n            local pids, err = ngx.worker.pids()\n            if err ~= nil then\n                return\n            end\n            local pid = ngx.worker.pid()\n            ngx.say(\"worker pid: \", pid)\n            local count = ngx.worker.count()\n            ngx.say(\"worker count: \", count)\n            ngx.say(\"worker pids count: \", #pids)\n            for i = 1, count do\n                if pids[i] == pid then\n                    ngx.say(\"worker pid is correct.\")\n                    return\n                end\n            end\n        }\n    }\n--- request\nGET /lua\n--- response_body_like\nworker pid: \\d+\nworker count: 4\nworker pids count: 4\nworker pid is correct\\.\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/122-worker.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4 - 4);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: content_by_lua + ngx.worker.exiting\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(\"worker exiting: \", ngx.worker.exiting())\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nworker exiting: false\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: content_by_lua + ngx.worker.pid\n--- config\n    location /lua {\n        content_by_lua '\n            local pid = ngx.worker.pid()\n            ngx.say(\"worker pid: \", pid)\n            if pid ~= tonumber(ngx.var.pid) then\n                ngx.say(\"worker pid is wrong.\")\n            else\n                ngx.say(\"worker pid is correct.\")\n            end\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like\nworker pid: \\d+\nworker pid is correct\\.\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: init_worker_by_lua + ngx.worker.pid\n--- http_config\n    init_worker_by_lua '\n        my_pid = ngx.worker.pid()\n    ';\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(\"worker pid: \", my_pid)\n            if my_pid ~= tonumber(ngx.var.pid) then\n                ngx.say(\"worker pid is wrong.\")\n            else\n                ngx.say(\"worker pid is correct.\")\n            end\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like\nworker pid: \\d+\nworker pid is correct\\.\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: content_by_lua + ngx.worker.pids\n--- config\n    location /lua {\n        content_by_lua '\n            local pids = ngx.worker.pids()\n            local pid = ngx.worker.pid()\n            ngx.say(\"worker pid: \", pid)\n            local count = ngx.worker.count()\n            if count ~= #pids then\n                ngx.say(\"worker pids is wrong.\")\n            end\n            for i = 1, count do\n                if pids[i] == pid then\n                    ngx.say(\"worker pid is correct.\")\n                    return\n                end\n            end\n            ngx.say(\"worker pid is wrong.\")\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like\nworker pid: \\d+\nworker pid is correct\\.\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/123-lua-path.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3 + 1);\n\n$ENV{LUA_PATH} = \"../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;/foo/bar/baz\";\n$ENV{LUA_CPATH} = \"/baz/bar/foo\";\n#no_diff();\n#no_long_string();\nmaster_on();\nno_shuffle();\ncheck_accum_error_log();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: LUA_PATH & LUA_CPATH env (code cache on)\n--- main_config\nenv LUA_PATH;\nenv LUA_CPATH;\n\n--- config\n    location /lua {\n        content_by_lua '\n            ngx.say(package.path)\n            ngx.say(package.cpath)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like\n(?:\\.\\.\\/lua-resty-core\\/lib\\/\\?\\.lua;\\.\\.\\/lua-resty-lrucache\\/lib\\/\\?\\.lua;){1,2}\\/foo\\/bar\\/baz\n/baz/bar/foo\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: LUA_PATH & LUA_CPATH env (code cache off)\n--- main_config\nenv LUA_PATH;\nenv LUA_CPATH;\n\n--- config\n    lua_code_cache off;\n    location /lua {\n        content_by_lua '\n            ngx.say(package.path)\n            ngx.say(package.cpath)\n        ';\n    }\n--- request\nGET /lua\n--- response_body_like\n(?:\\.\\.\\/lua-resty-core\\/lib\\/\\?\\.lua;\\.\\.\\/lua-resty-lrucache\\/lib\\/\\?\\.lua;){1,2}\\/foo\\/bar\\/baz\n/baz/bar/foo\n\n--- no_error_log\n[error]\n--- error_log eval\nqr/\\[alert\\] .*? lua_code_cache is off/\n"
  },
  {
    "path": "t/124-init-worker.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\nmaster_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 4 + 4);\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\nour $ServerRoot = server_root();\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: set a global lua var\n--- http_config\n    init_worker_by_lua '\n        foo = ngx.md5(\"hello world\")\n    ';\n--- config\n    location /t {\n        content_by_lua '\n            ngx.say(\"foo = \", foo)\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nfoo = 5eb63bbbe01eeed093cb22bb8f5acdc3\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: no ngx.say()\n--- http_config\n    init_worker_by_lua '\n        ngx.say(\"hello\")\n    ';\n--- config\n    location /t {\n        content_by_lua '\n            ngx.say(\"foo = \", foo)\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nfoo = nil\n--- error_log\nAPI disabled in the context of init_worker_by_lua*\n\n\n\n=== TEST 3: timer.at\n--- http_config\n    init_worker_by_lua '\n        _G.my_counter = 0\n        local function warn(...)\n            ngx.log(ngx.WARN, ...)\n        end\n        local function handler(premature)\n            warn(\"timer expired (premature: \", premature, \"; counter: \",\n                 _G.my_counter, \")\")\n            _G.my_counter = _G.my_counter + 1\n        end\n        local ok, err = ngx.timer.at(0, handler)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to create timer: \", err)\n        end\n        warn(\"created timer: \", ok)\n    ';\n--- config\n    location /t {\n        content_by_lua '\n            -- ngx.sleep(0.001)\n            ngx.say(\"my_counter = \", _G.my_counter)\n            _G.my_counter = _G.my_counter + 1\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nmy_counter = 1\n--- grep_error_log eval: qr/warn\\(\\): [^,]*/\n--- grep_error_log_out\nwarn(): created timer: 1\nwarn(): timer expired (premature: false; counter: 0)\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: timer.at + cosocket\n--- http_config\n    init_worker_by_lua '\n        _G.done = false\n        local function warn(...)\n            ngx.log(ngx.WARN, ...)\n        end\n        local function error(...)\n            ngx.log(ngx.ERR, ...)\n        end\n        local function handler(premature)\n            warn(\"timer expired (premature: \", premature, \")\")\n\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                error(\"failed to connect: \", err)\n                _G.done = true\n                return\n            end\n\n            local req = \"flush_all\\\\r\\\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                error(\"failed to send request: \", err)\n                _G.done = true\n                return\n            end\n\n            warn(\"request sent: \", bytes)\n\n            local line, err, part = sock:receive()\n            if line then\n                warn(\"received: \", line)\n            else\n                error(\"failed to receive a line: \", err, \" [\", part, \"]\")\n            end\n            _G.done = true\n        end\n\n        local ok, err = ngx.timer.at(0, handler)\n        if not ok then\n            error(\"failed to create timer: \", err)\n        end\n        warn(\"created timer: \", ok)\n    ';\n--- config\n    location = /t {\n        content_by_lua '\n            local waited = 0\n            local sleep = ngx.sleep\n            while not _G.done do\n                local delay = 0.001\n                sleep(delay)\n                waited = waited + delay\n                if waited > 1 then\n                    ngx.say(\"timed out\")\n                    return\n                end\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nok\n--- grep_error_log eval: qr/warn\\(\\): [^,]*/\n--- grep_error_log_out\nwarn(): created timer: 1\nwarn(): timer expired (premature: false)\nwarn(): request sent: 11\nwarn(): received: OK\n\n--- log_level: debug\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket send timeout: 60000\nlua tcp socket read timeout: 60000\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: init_worker_by_lua_file (simple global var)\n--- http_config\n    init_worker_by_lua_file html/foo.lua;\n--- config\n    location /t {\n        content_by_lua '\n            ngx.say(\"foo = \", foo)\n        ';\n    }\n--- user_files\n>>> foo.lua\nfoo = ngx.md5(\"hello world\")\n--- request\n    GET /t\n--- response_body\nfoo = 5eb63bbbe01eeed093cb22bb8f5acdc3\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: timer.at + cosocket (by_lua_file)\n--- main_config\nenv TEST_NGINX_MEMCACHED_PORT;\n--- http_config\n    init_worker_by_lua_file html/foo.lua;\n--- user_files\n>>> foo.lua\n_G.done = false\nlocal function warn(...)\n    ngx.log(ngx.WARN, ...)\nend\nlocal function error(...)\n    ngx.log(ngx.ERR, ...)\nend\nlocal function handler(premature)\n    warn(\"timer expired (premature: \", premature, \")\")\n\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:connect(\"127.0.0.1\",\n                                 os.getenv(\"TEST_NGINX_MEMCACHED_PORT\"))\n    if not ok then\n        error(\"failed to connect: \", err)\n        _G.done = true\n        return\n    end\n\n    local req = \"flush_all\\r\\n\"\n\n    local bytes, err = sock:send(req)\n    if not bytes then\n        error(\"failed to send request: \", err)\n        _G.done = true\n        return\n    end\n\n    warn(\"request sent: \", bytes)\n\n    local line, err, part = sock:receive()\n    if line then\n        warn(\"received: \", line)\n    else\n        error(\"failed to receive a line: \", err, \" [\", part, \"]\")\n    end\n    _G.done = true\nend\n\nlocal ok, err = ngx.timer.at(0, handler)\nif not ok then\n    error(\"failed to create timer: \", err)\nend\nwarn(\"created timer: \", ok)\n\n--- config\n    location = /t {\n        content_by_lua '\n            local waited = 0\n            local sleep = ngx.sleep\n            while not _G.done do\n                local delay = 0.001\n                sleep(delay)\n                waited = waited + delay\n                if waited > 1 then\n                    ngx.say(\"timed out\")\n                    return\n                end\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nok\n--- grep_error_log eval: qr/warn\\(\\): [^,]*/\n--- grep_error_log_out\nwarn(): created timer: 1\nwarn(): timer expired (premature: false)\nwarn(): request sent: 11\nwarn(): received: OK\n\n--- log_level: debug\n--- error_log\nlua tcp socket connect timeout: 60000\nlua tcp socket send timeout: 60000\nlua tcp socket read timeout: 60000\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: ngx.ctx\n--- http_config\n    init_worker_by_lua '\n        ngx.ctx.foo = \"hello world\"\n        local function warn(...)\n            ngx.log(ngx.WARN, ...)\n        end\n        warn(\"foo = \", ngx.ctx.foo)\n    ';\n--- config\n    location /t {\n        echo ok;\n    }\n--- request\n    GET /t\n--- response_body\nok\n--- grep_error_log eval: qr/warn\\(\\): [^,]*/\n--- grep_error_log_out\nwarn(): foo = hello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: print\n--- http_config\n    init_worker_by_lua '\n        print(\"md5 = \", ngx.md5(\"hello world\"))\n    ';\n--- config\n    location /t {\n        echo ok;\n    }\n--- request\n    GET /t\n--- response_body\nok\n--- no_error_log\n[error]\n--- error_log\nmd5 = 5eb63bbbe01eeed093cb22bb8f5acdc3\n\n\n\n=== TEST 9: unescape_uri\n--- http_config\n    init_worker_by_lua '\n        local function warn(...)\n            ngx.log(ngx.WARN, ...)\n        end\n\n        warn(ngx.unescape_uri(\"hello%20world\"))\n    ';\n--- config\n    location /t {\n        echo ok;\n    }\n--- request\n    GET /t\n--- response_body\nok\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/warn\\(\\): [^,]*/\n--- grep_error_log_out\nwarn(): hello world\n\n\n\n=== TEST 10: escape_uri\n--- http_config\n    init_worker_by_lua '\n        local function warn(...)\n            ngx.log(ngx.WARN, ...)\n        end\n\n        warn(ngx.escape_uri(\"hello world\"))\n    ';\n--- config\n    location /t {\n        echo ok;\n    }\n--- request\n    GET /t\n--- response_body\nok\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/warn\\(\\): [^,]*/\n--- grep_error_log_out\nwarn(): hello%20world\n\n\n\n=== TEST 11: ngx.re\n--- http_config\n    init_worker_by_lua '\n        local function warn(...)\n            ngx.log(ngx.WARN, ...)\n        end\n\n        warn((ngx.re.sub(\"hello world\", \"world\", \"XXX\", \"jo\")))\n    ';\n--- config\n    location /t {\n        echo ok;\n    }\n--- request\n    GET /t\n--- response_body\nok\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/warn\\(\\): [^,]*/\n--- grep_error_log_out\nwarn(): hello XXX\n\n\n\n=== TEST 12: ngx.http_time\n--- http_config\n    init_worker_by_lua '\n        local function warn(...)\n            ngx.log(ngx.WARN, ...)\n        end\n\n        warn(ngx.http_time(5678))\n    ';\n--- config\n    location /t {\n        echo ok;\n    }\n--- request\n    GET /t\n--- response_body\nok\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/warn\\(\\): .*?(?=, context)/\n--- grep_error_log_out\nwarn(): Thu, 01 Jan 1970 01:34:38 GMT\n\n\n\n=== TEST 13: cosocket with resolver\n--- timeout: 10\n--- http_config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    init_worker_by_lua '\n        -- global\n        logs = \"\"\n        done = false\n        local function say(...)\n            logs = logs .. table.concat({...}) .. \"\\\\n\"\n        end\n\n        local function handler()\n            local sock = ngx.socket.tcp()\n            local port = 80\n            local ok, err = sock:connect(\"agentzh.org\", port)\n            if not ok then\n                say(\"failed to connect: \", err)\n                done = true\n                return\n            end\n\n            say(\"connected: \", ok)\n\n            local req = \"GET / HTTP/1.0\\\\r\\\\nHost: agentzh.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                say(\"failed to send request: \", err)\n                done = true\n                return\n            end\n\n            say(\"request sent: \", bytes)\n\n            local line, err = sock:receive()\n            if line then\n                say(\"first line received: \", line)\n\n            else\n                say(\"failed to receive the first line: \", err)\n            end\n\n            line, err = sock:receive()\n            if line then\n                say(\"second line received: \", line)\n\n            else\n                say(\"failed to receive the second line: \", err)\n            end\n\n            done = true\n        end\n\n        local ok, err = ngx.timer.at(0, handler)\n        if not ok then\n            say(\"failed to create timer: \", err)\n        else\n            say(\"timer created\")\n        end\n    ';\n\n--- config\n    location = /t {\n        content_by_lua '\n            local i = 0\n            while not done and i < 3000 do\n                ngx.sleep(0.001)\n                i = i + 1\n            end\n            ngx.print(logs)\n        ';\n    }\n--- request\nGET /t\n--- response_body_like\nconnected: 1\nrequest sent: 56\nfirst line received: HTTP\\/1\\.1 200 OK\nsecond line received: (?:Date|Server): .*?\n--- no_error_log\n[error]\n--- timeout: 10\n--- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 14: connection refused (tcp) - log_errors on by default\n--- http_config\n    init_worker_by_lua '\n        logs = \"\"\n        done = false\n        local function say(...)\n            logs = logs .. table.concat{...} .. \"\\\\n\"\n        end\n\n        local function handler()\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", 16787)\n            if not ok then\n                say(\"failed to connect: \", err)\n            else\n                say(\"connect: \", ok, \" \", err)\n            end\n            done = true\n        end\n\n        local ok, err = ngx.timer.at(0, handler)\n        if not ok then\n            say(\"failed to create timer: \", err)\n        else\n            say(\"timer created\")\n        end\n    ';\n\n--- config\n    location = /t {\n        content_by_lua '\n            local i = 0\n            while not done and i < 1000 do\n                ngx.sleep(0.001)\n                i = i + 1\n            end\n            ngx.print(logs)\n        ';\n    }\n\n--- request\n    GET /t\n--- response_body\ntimer created\nfailed to connect: connection refused\n--- error_log eval\nqr/connect\\(\\) failed \\(\\d+: Connection refused\\), context: ngx\\.timer$/\n\n\n\n=== TEST 15: connection refused (tcp) - log_errors explicitly on\n--- http_config\n    lua_socket_log_errors on;\n    init_worker_by_lua '\n        logs = \"\"\n        done = false\n        local function say(...)\n            logs = logs .. table.concat{...} .. \"\\\\n\"\n        end\n\n        local function handler()\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", 16787)\n            if not ok then\n                say(\"failed to connect: \", err)\n            else\n                say(\"connect: \", ok, \" \", err)\n            end\n            done = true\n        end\n\n        local ok, err = ngx.timer.at(0, handler)\n        if not ok then\n            say(\"failed to create timer: \", err)\n        else\n            say(\"timer created\")\n        end\n    ';\n\n--- config\n    location = /t {\n        content_by_lua '\n            local i = 0\n            while not done and i < 1000 do\n                ngx.sleep(0.001)\n                i = i + 1\n            end\n            ngx.print(logs)\n        ';\n    }\n\n--- request\n    GET /t\n--- response_body\ntimer created\nfailed to connect: connection refused\n--- error_log eval\nqr/connect\\(\\) failed \\(\\d+: Connection refused\\)/\n\n\n\n=== TEST 16: connection refused (tcp) - log_errors explicitly off\n--- http_config\n    lua_socket_log_errors off;\n    init_worker_by_lua '\n        logs = \"\"\n        done = false\n        local function say(...)\n            logs = logs .. table.concat{...} .. \"\\\\n\"\n        end\n\n        local function handler()\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", 16787)\n            if not ok then\n                say(\"failed to connect: \", err)\n            else\n                say(\"connect: \", ok, \" \", err)\n            end\n            done = true\n        end\n\n        local ok, err = ngx.timer.at(0, handler)\n        if not ok then\n            say(\"failed to create timer: \", err)\n        else\n            say(\"timer created\")\n        end\n    ';\n\n--- config\n    location = /t {\n        content_by_lua '\n            local i = 0\n            while not done and i < 1000 do\n                ngx.sleep(0.001)\n                i = i + 1\n            end\n            ngx.print(logs)\n        ';\n    }\n\n--- request\n    GET /t\n--- response_body\ntimer created\nfailed to connect: connection refused\n--- no_error_log eval\n[\n'qr/connect\\(\\) failed \\(\\d+: Connection refused\\)/',\n'[error]',\n]\n\n\n\n=== TEST 17: init_by_lua + proxy_temp_path which has side effects in cf->cycle->paths\n--- http_config eval\nqq{\n    proxy_temp_path $::ServerRoot/proxy_temp;\n    init_worker_by_lua '\n        local a = 2 + 3\n    ';\n}\n--- config\n    location /t {\n        echo ok;\n    }\n--- request\n    GET /t\n--- response_body\nok\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 18: syslog error log\n--- http_config\n    #error_log syslog:server=127.0.0.1:12345 error;\n    init_worker_by_lua '\n        done = false\n        os.execute(\"sleep 0.1\")\n        ngx.log(ngx.ERR, \"Bad bad bad\")\n        done = true\n    ';\n--- config\n    location /t {\n        content_by_lua '\n            while not done do\n                ngx.sleep(0.001)\n            end\n            ngx.say(\"ok\")\n        ';\n    }\n--- log_level: error\n--- error_log_file: syslog:server=127.0.0.1:$TEST_NGINX_RAND_PORT_1\n--- udp_listen: $TEST_NGINX_RAND_PORT_1\n--- udp_query eval: qr/Bad bad bad/\n--- udp_reply: hello\n--- wait: 0.1\n--- request\n    GET /t\n--- response_body\nok\n--- error_log\nBad bad bad\n--- skip_nginx: 4: < 1.7.1\n\n\n\n=== TEST 19: fake module calls ngx_http_conf_get_module_srv_conf in its merge_srv_conf callback (GitHub issue #554)\nThis also affects merge_loc_conf\n--- http_config\n    init_worker_by_lua return;\n--- config\n    location = /t {\n        return 200 ok;\n    }\n--- request\nGET /t\n--- response_body chomp\nok\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: destroy Lua VM in cache processes (without privileged agent or shdict)\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;\";\n\n    proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:1m;\n\n    #lua_shared_dict dummy 500k;\n\n    init_by_lua_block {\n        require \"resty.core.regex\"\n        assert(ngx.re.match(\"hello, world\", [[hello, \\w+]], \"joi\"))\n        assert(ngx.re.match(\"hi, world\", [[hi, \\w+]], \"ji\"))\n    }\n\n--- config\n    location = /t {\n        return 200;\n    }\n--- request\n    GET /t\n--- grep_error_log eval: qr/lua close the global Lua VM \\S+ in the cache helper process \\d+|lua close the global Lua VM \\S+$/\n--- grep_error_log_out eval\nqr/\\A(?:lua close the global Lua VM ([0-9A-F]+) in the cache helper process \\d+\nlua close the global Lua VM \\1\nlua close the global Lua VM \\1 in the cache helper process \\d+\nlua close the global Lua VM \\1\n|lua close the global Lua VM ([0-9A-F]+) in the cache helper process \\d+\nlua close the global Lua VM \\2 in the cache helper process \\d+\nlua close the global Lua VM \\2\nlua close the global Lua VM \\2\n|lua close the global Lua VM ([0-9A-F]+)\nlua close the global Lua VM \\3 in the cache helper process \\d+\nlua close the global Lua VM \\3\nlua close the global Lua VM \\3 in the cache helper process \\d+\n|lua close the global Lua VM ([0-9A-F]+)\nlua close the global Lua VM \\4 in the cache helper process \\d+\nlua close the global Lua VM \\4 in the cache helper process \\d+\nlua close the global Lua VM \\4\n)(?:lua close the global Lua VM [0-9A-F]+\n)*\\z/\n--- no_error_log\n[error]\nstart privileged agent process\n\n\n\n=== TEST 21: destroy Lua VM in cache processes (without privileged agent but with shdict)\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;\";\n\n    proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:1m;\n\n    lua_shared_dict dummy 500k;\n\n    init_by_lua_block {\n        require \"resty.core.regex\"\n        assert(ngx.re.match(\"hello, world\", [[hello, \\w+]], \"joi\"))\n        assert(ngx.re.match(\"hi, world\", [[hi, \\w+]], \"ji\"))\n    }\n\n--- config\n    location = /t {\n        return 200;\n    }\n--- request\n    GET /t\n--- grep_error_log eval: qr/lua close the global Lua VM \\S+ in the cache helper process \\d+|lua close the global Lua VM \\S+$/\n--- grep_error_log_out eval\nqr/\\A(?:lua close the global Lua VM ([0-9A-F]+) in the cache helper process \\d+\nlua close the global Lua VM \\1\nlua close the global Lua VM \\1 in the cache helper process \\d+\nlua close the global Lua VM \\1\n|lua close the global Lua VM ([0-9A-F]+) in the cache helper process \\d+\nlua close the global Lua VM \\2 in the cache helper process \\d+\nlua close the global Lua VM \\2\nlua close the global Lua VM \\2\n|lua close the global Lua VM ([0-9A-F]+)\nlua close the global Lua VM \\3 in the cache helper process \\d+\nlua close the global Lua VM \\3\nlua close the global Lua VM \\3 in the cache helper process \\d+\n)(?:lua close the global Lua VM [0-9A-F]+\n|lua close the global Lua VM ([0-9A-F]+)\nlua close the global Lua VM \\4 in the cache helper process \\d+\nlua close the global Lua VM \\4 in the cache helper process \\d+\nlua close the global Lua VM \\4 \nlua close the global Lua VM \\4\n)*\\z/\n--- no_error_log\n[error]\nstart privileged agent process\n\n\n\n=== TEST 22: destroy Lua VM in cache processes (with privileged agent)\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;\";\n\n    #lua_shared_dict dogs 1m;\n\n    proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:1m;\n\n    init_by_lua_block {\n        assert(require \"ngx.process\".enable_privileged_agent())\n        require \"resty.core.regex\"\n        assert(ngx.re.match(\"hello, world\", [[hello, \\w+]], \"joi\"))\n        assert(ngx.re.match(\"hi, world\", [[hi, \\w+]], \"ji\"))\n    }\n\n--- config\n    location = /t {\n        return 200;\n    }\n--- request\n    GET /t\n--- grep_error_log eval: qr/lua close the global Lua VM \\S+ in the cache helper process \\d+|lua close the global Lua VM \\S+$/\n--- grep_error_log_out eval\nqr/\\A(?:lua close the global Lua VM ([0-9A-F]+) in the cache helper process \\d+\nlua close the global Lua VM \\1\nlua close the global Lua VM \\1 in the cache helper process \\d+\nlua close the global Lua VM \\1\n|lua close the global Lua VM ([0-9A-F]+) in the cache helper process \\d+\nlua close the global Lua VM \\2 in the cache helper process \\d+\nlua close the global Lua VM \\2\nlua close the global Lua VM \\2\n|lua close the global Lua VM ([0-9A-F]+)\nlua close the global Lua VM \\3 in the cache helper process \\d+\nlua close the global Lua VM \\3\nlua close the global Lua VM \\3 in the cache helper process \\d+\n|lua close the global Lua VM ([0-9A-F]+)\nlua close the global Lua VM \\4 in the cache helper process \\d+\nlua close the global Lua VM \\4 in the cache helper process \\d+\nlua close the global Lua VM \\4\n)(?:lua close the global Lua VM [0-9A-F]+\n)*\\z/\n--- error_log eval\nqr/start privileged agent process \\d+/\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: destroy Lua VM in cache processes (with init worker and privileged agent)\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;\";\n\n    #lua_shared_dict dogs 1m;\n\n    proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:1m;\n\n    init_by_lua_block {\n        assert(require \"ngx.process\".enable_privileged_agent())\n        require \"resty.core.regex\"\n        assert(ngx.re.match(\"hello, world\", [[hello, \\w+]], \"joi\"))\n        assert(ngx.re.match(\"hi, world\", [[hi, \\w+]], \"ji\"))\n    }\n\n    init_worker_by_lua_block {\n        ngx.log(ngx.WARN, \"hello from init worker by lua\")\n    }\n\n--- config\n    location = /t {\n        return 200;\n    }\n--- request\n    GET /t\n--- grep_error_log eval: qr/hello from init worker by lua/\n--- grep_error_log_out\nhello from init worker by lua\nhello from init worker by lua\n\n--- error_log eval\n[\nqr/start privileged agent process \\d+$/,\nqr/lua close the global Lua VM ([0-9A-F]+) in the cache helper process \\d+$/,\nqr/lua close the global Lua VM ([0-9A-F]+)$/,\n]\n--- no_error_log\n[error]\n\n\n\n=== TEST 24: destroy Lua VM in cache processes (with init worker but without privileged agent)\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;\";\n\n    #lua_shared_dict dogs 1m;\n\n    proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:1m;\n\n    init_by_lua_block {\n        require \"resty.core.regex\"\n        assert(ngx.re.match(\"hello, world\", [[hello, \\w+]], \"joi\"))\n        assert(ngx.re.match(\"hi, world\", [[hi, \\w+]], \"ji\"))\n    }\n\n    init_worker_by_lua_block {\n        ngx.log(ngx.WARN, \"hello from init worker by lua\")\n    }\n\n--- config\n    location = /t {\n        return 200;\n    }\n--- request\n    GET /t\n\n--- grep_error_log eval: qr/hello from init worker by lua/\n--- grep_error_log_out\nhello from init worker by lua\n\n--- error_log eval\n[\nqr/lua close the global Lua VM ([0-9A-F]+) in the cache helper process \\d+$/,\nqr/lua close the global Lua VM ([0-9A-F]+)$/,\n]\n--- no_error_log\n[error]\nstart privileged agent process\n\n\n\n=== TEST 25: syntax error in init_worker_by_lua_block\n--- http_config\n    init_worker_by_lua_block {\n        ngx.log(ngx.debug, \"pass\")\n        error(\"failed to init\"\n        ngx.log(ngx.debug, \"unreachable\")\n    }\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.say(\"hello world\")\n        }\n    }\n--- request\n    GET /t\n--- response_body\nhello world\n--- error_log\ninit_worker_by_lua error: init_worker_by_lua(nginx.conf:25):4: ')' expected (to close '(' at line 3) near 'ngx'\n--- no_error_log\nno_such_error_log\n\n\n\n=== TEST 26: syntax error in init_worker_by_lua_file\n--- http_config\n    init_worker_by_lua_file html/init.lua;\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.say(\"hello world\")\n        }\n    }\n--- user_files\n>>> init.lua\n    ngx.log(ngx.debug, \"pass\")\n    error(\"failed to init\"\n    ngx.log(ngx.debug, \"unreachable\")\n\n--- request\n    GET /t\n--- response_body\nhello world\n--- error_log eval\nqr|init_worker_by_lua_file error: .*?t/servroot\\w*/html/init.lua:3: '\\)' expected \\(to close '\\(' at line 2\\) near 'ngx'|\n--- no_error_log\nno_such_error_log\n"
  },
  {
    "path": "t/125-configure-args.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: nginx configure\n--- config\n    location /configure_args {\n        content_by_lua '\n            ngx.say(ngx.config.nginx_configure())\n        ';\n    }\n--- request\nGET /configure_args\n--- response_body_like chop\n^\\s*\\-\\-[^-]+\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/126-shdict-frag.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\n#repeat_each(2);\n\nplan tests => repeat_each() * 39;\n\n#no_diff();\nno_long_string();\n#master_on();\n#workers(2);\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: merge 2 single-page free blocks (forcibly evicted, merge forward)\n--- http_config\n    lua_shared_dict dogs 20k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            local function check_key(key)\n                local res, err = dogs:get(key)\n                if res then\n                    ngx.say(\"found \", key, \": \", #res)\n                else\n                    if not err then\n                        ngx.say(key, \" not found\")\n                    else\n                        ngx.say(\"failed to fetch key: \", err)\n                    end\n                end\n            end\n\n            local function set_key(key, value)\n                local ok, err, force = dogs:set(key, value)\n                if ok then\n                    ngx.print(\"successfully set \", key)\n                    if force then\n                        ngx.say(\" with force.\")\n                    else\n                        ngx.say(\".\")\n                    end\n                else\n                    ngx.say(\"failed to set \", key, \": \", err)\n                end\n            end\n\n            for i = 1, 2 do\n                set_key(\"foo\", string.rep(\"a\", 4000))\n                set_key(\"bar\", string.rep(\"b\", 4001))\n                set_key(\"baz\", string.rep(\"c\", 8102))\n\n                check_key(\"foo\")\n                check_key(\"bar\")\n                check_key(\"baz\")\n            end\n\n            collectgarbage()\n        ';\n    }\n--- request\nGET /test\n--- stap\nglobal first_time = 1\nglobal active = 1\n\nF(ngx_http_lua_shdict_init_zone) {\n    active = 0\n}\n\nF(ngx_http_lua_shdict_init_zone).return {\n    active = 1\n}\n\nF(ngx_slab_alloc_pages) {\n    if (first_time) {\n        printf(\"total pages: %d\\n\", $pool->pages->slab)\n        first_time = 0\n    }\n    if (active) {\n        printf(\"alloc pages: %d\", $pages)\n        //print_ubacktrace()\n    } else {\n        printf(\"init zone alloc pages: %d\", $pages)\n    }\n}\n\nF(ngx_slab_alloc_pages).return {\n    if ($return) {\n        printf(\" ok\\n\")\n\n    } else {\n        printf(\" NOT OK\\n\")\n    }\n}\n\nF(ngx_slab_free_pages) {\n    printf(\"free pages: %d\\n\", $pages)\n}\n\n--- stap_out\ntotal pages: 4\ninit zone alloc pages: 1 ok\ninit zone alloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 ok\nalloc pages: 1 NOT OK\nfree pages: 2\nalloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 ok\n\n--- response_body\nsuccessfully set foo.\nsuccessfully set bar.\nsuccessfully set baz with force.\nfoo not found\nbar not found\nfound baz: 8102\nsuccessfully set foo with force.\nsuccessfully set bar.\nsuccessfully set baz with force.\nfoo not found\nbar not found\nfound baz: 8102\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: merge 2 single-page free slabs (forcibly evicted, merge backward)\n--- http_config\n    lua_shared_dict dogs 20k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            local function check_key(key)\n                local res, err = dogs:get(key)\n                if res then\n                    ngx.say(\"found \", key, \": \", #res)\n                else\n                    if not err then\n                        ngx.say(key, \" not found\")\n                    else\n                        ngx.say(\"failed to fetch key: \", err)\n                    end\n                end\n            end\n\n            local function set_key(key, value)\n                local ok, err, force = dogs:set(key, value)\n                if ok then\n                    ngx.print(\"successfully set \", key)\n                    if force then\n                        ngx.say(\" with force.\")\n                    else\n                        ngx.say(\".\")\n                    end\n                else\n                    ngx.say(\"failed to set \", key, \": \", err)\n                end\n            end\n\n            for i = 1, 2 do\n                set_key(\"foo\", string.rep(\"a\", 4000))\n                set_key(\"bar\", string.rep(\"b\", 4001))\n                check_key(\"foo\")\n                set_key(\"baz\", string.rep(\"c\", 8102))\n\n                check_key(\"foo\")\n                check_key(\"bar\")\n                check_key(\"baz\")\n            end\n\n            collectgarbage()\n        ';\n    }\n--- request\nGET /test\n--- stap\nglobal first_time = 1\nglobal active = 1\n\nF(ngx_http_lua_shdict_init_zone) {\n    active = 0\n}\n\nF(ngx_http_lua_shdict_init_zone).return {\n    active = 1\n}\n\nF(ngx_slab_alloc_pages) {\n    if (first_time) {\n        printf(\"total pages: %d\\n\", $pool->pages->slab)\n        first_time = 0\n    }\n    if (active) {\n        printf(\"alloc pages: %d\", $pages)\n        //print_ubacktrace()\n    } else {\n        printf(\"init zone alloc pages: %d\", $pages)\n    }\n}\n\nF(ngx_slab_alloc_pages).return {\n    if ($return) {\n        printf(\" ok\\n\")\n\n    } else {\n        printf(\" NOT OK\\n\")\n    }\n}\n\nF(ngx_slab_free_pages) {\n    printf(\"free pages: %d\\n\", $pages)\n}\n\n--- stap_out\ntotal pages: 4\ninit zone alloc pages: 1 ok\ninit zone alloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 ok\nalloc pages: 1 NOT OK\nfree pages: 2\nalloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 ok\n\n--- response_body\nsuccessfully set foo.\nsuccessfully set bar.\nfound foo: 4000\nsuccessfully set baz with force.\nfoo not found\nbar not found\nfound baz: 8102\nsuccessfully set foo with force.\nsuccessfully set bar.\nfound foo: 4000\nsuccessfully set baz with force.\nfoo not found\nbar not found\nfound baz: 8102\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: merge 3 single-page free slabs (actively deleted, merge backward AND forward)\n--- http_config\n    lua_shared_dict dogs 25k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            local function check_key(key)\n                local res, err = dogs:get(key)\n                if res then\n                    ngx.say(\"found \", key, \": \", #res)\n                else\n                    if not err then\n                        ngx.say(key, \" not found\")\n                    else\n                        ngx.say(\"failed to fetch key: \", err)\n                    end\n                end\n            end\n\n            local function set_key(key, value)\n                local ok, err, force = dogs:set(key, value)\n                if ok then\n                    ngx.print(\"successfully set \", key)\n                    if force then\n                        ngx.say(\" with force.\")\n                    else\n                        ngx.say(\".\")\n                    end\n                else\n                    ngx.say(\"failed to set \", key, \": \", err)\n                end\n            end\n\n            local function safe_set_key(key, value)\n                local ok, err = dogs:safe_set(key, value)\n                if ok then\n                    ngx.say(\"successfully safe set \", key)\n                else\n                    ngx.say(\"failed to safe set \", key, \": \", err)\n                end\n            end\n\n            for i = 1, 2 do\n                set_key(\"foo\", string.rep(\"a\", 4000))\n                set_key(\"bar\", string.rep(\"b\", 4001))\n                set_key(\"baz\", string.rep(\"c\", 4002))\n\n                check_key(\"foo\")\n                check_key(\"bar\")\n                check_key(\"baz\")\n\n                dogs:delete(\"foo\")\n                safe_set_key(\"blah\", string.rep(\"a\", 8100))\n                dogs:delete(\"baz\")\n                safe_set_key(\"blah\", string.rep(\"a\", 8100))\n                dogs:delete(\"bar\")\n                safe_set_key(\"blah\", string.rep(\"a\", 12010))\n            end\n\n            collectgarbage()\n        ';\n    }\n--- request\nGET /test\n--- stap\nglobal first_time = 1\nglobal active = 1\n\nF(ngx_http_lua_shdict_init_zone) {\n    active = 0\n}\n\nF(ngx_http_lua_shdict_init_zone).return {\n    active = 1\n}\n\nF(ngx_slab_alloc_pages) {\n    if (first_time) {\n        printf(\"total pages: %d\\n\", $pool->pages->slab)\n        first_time = 0\n    }\n    if (active) {\n        printf(\"alloc pages: %d\", $pages)\n        //print_ubacktrace()\n    } else {\n        printf(\"init zone alloc pages: %d\", $pages)\n    }\n}\n\nF(ngx_slab_alloc_pages).return {\n    if ($return) {\n        printf(\" ok\\n\")\n\n    } else {\n        printf(\" NOT OK\\n\")\n    }\n}\n\nF(ngx_slab_free_pages) {\n    printf(\"free pages: %d\\n\", $pages)\n}\n\n--- stap_out\ntotal pages: 5\ninit zone alloc pages: 1 ok\ninit zone alloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 1 ok\nfree pages: 1\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 3 ok\nalloc pages: 1 NOT OK\nfree pages: 3\nalloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 1 ok\nfree pages: 1\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 3 ok\n\n--- response_body\nsuccessfully set foo.\nsuccessfully set bar.\nsuccessfully set baz.\nfound foo: 4000\nfound bar: 4001\nfound baz: 4002\nfailed to safe set blah: no memory\nfailed to safe set blah: no memory\nsuccessfully safe set blah\nsuccessfully set foo with force.\nsuccessfully set bar.\nsuccessfully set baz.\nfound foo: 4000\nfound bar: 4001\nfound baz: 4002\nfailed to safe set blah: no memory\nfailed to safe set blah: no memory\nsuccessfully safe set blah\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: merge one single-page block backward, but no more\n--- http_config\n    lua_shared_dict dogs 25k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            local function check_key(key)\n                local res, err = dogs:get(key)\n                if res then\n                    ngx.say(\"found \", key, \": \", #res)\n                else\n                    if not err then\n                        ngx.say(key, \" not found\")\n                    else\n                        ngx.say(\"failed to fetch key: \", err)\n                    end\n                end\n            end\n\n            local function set_key(key, value)\n                local ok, err, force = dogs:set(key, value)\n                if ok then\n                    ngx.print(\"successfully set \", key)\n                    if force then\n                        ngx.say(\" with force.\")\n                    else\n                        ngx.say(\".\")\n                    end\n                else\n                    ngx.say(\"failed to set \", key, \": \", err)\n                end\n            end\n\n            local function safe_set_key(key, value)\n                local ok, err = dogs:safe_set(key, value)\n                if ok then\n                    ngx.say(\"successfully safe set \", key)\n                else\n                    ngx.say(\"failed to safe set \", key, \": \", err)\n                end\n            end\n\n            for i = 1, 1 do\n                set_key(\"foo\", string.rep(\"a\", 4000))\n                set_key(\"bar\", string.rep(\"b\", 4001))\n                set_key(\"baz\", string.rep(\"c\", 4002))\n\n                check_key(\"foo\")\n                check_key(\"bar\")\n                check_key(\"baz\")\n\n                dogs:delete(\"bar\")\n                safe_set_key(\"blah\", string.rep(\"a\", 8100))\n                dogs:delete(\"baz\")\n                safe_set_key(\"blah\", string.rep(\"a\", 8100))\n                check_key(\"foo\")\n                dogs:delete(\"foo\")\n                check_key(\"blah\")\n            end\n\n            collectgarbage()\n        ';\n    }\n--- request\nGET /test\n--- stap\nglobal first_time = 1\nglobal active = 1\n\nF(ngx_http_lua_shdict_init_zone) {\n    active = 0\n}\n\nF(ngx_http_lua_shdict_init_zone).return {\n    active = 1\n}\n\nF(ngx_slab_alloc_pages) {\n    if (first_time) {\n        printf(\"total pages: %d\\n\", $pool->pages->slab)\n        first_time = 0\n    }\n    if (active) {\n        printf(\"alloc pages: %d\", $pages)\n        //print_ubacktrace()\n    } else {\n        printf(\"init zone alloc pages: %d\", $pages)\n    }\n}\n\nF(ngx_slab_alloc_pages).return {\n    if ($return) {\n        printf(\" ok\\n\")\n\n    } else {\n        printf(\" NOT OK\\n\")\n    }\n}\n\nF(ngx_slab_free_pages) {\n    printf(\"free pages: %d\\n\", $pages)\n}\n\n--- stap_out\ntotal pages: 5\ninit zone alloc pages: 1 ok\ninit zone alloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 1 ok\nfree pages: 1\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 ok\nfree pages: 1\n\n--- response_body\nsuccessfully set foo.\nsuccessfully set bar.\nsuccessfully set baz.\nfound foo: 4000\nfound bar: 4001\nfound baz: 4002\nfailed to safe set blah: no memory\nsuccessfully safe set blah\nfound foo: 4000\nfound blah: 8100\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: merge one single-page block forward, but no more\n--- http_config\n    lua_shared_dict dogs 25k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            local function check_key(key)\n                local res, err = dogs:get(key)\n                if res then\n                    ngx.say(\"found \", key, \": \", #res)\n                else\n                    if not err then\n                        ngx.say(key, \" not found\")\n                    else\n                        ngx.say(\"failed to fetch key: \", err)\n                    end\n                end\n            end\n\n            local function set_key(key, value)\n                local ok, err, force = dogs:set(key, value)\n                if ok then\n                    ngx.print(\"successfully set \", key)\n                    if force then\n                        ngx.say(\" with force.\")\n                    else\n                        ngx.say(\".\")\n                    end\n                else\n                    ngx.say(\"failed to set \", key, \": \", err)\n                end\n            end\n\n            local function safe_set_key(key, value)\n                local ok, err = dogs:safe_set(key, value)\n                if ok then\n                    ngx.say(\"successfully safe set \", key)\n                else\n                    ngx.say(\"failed to safe set \", key, \": \", err)\n                end\n            end\n\n            for i = 1, 1 do\n                set_key(\"foo\", string.rep(\"a\", 4000))\n                set_key(\"bar\", string.rep(\"b\", 4001))\n                set_key(\"baz\", string.rep(\"c\", 4002))\n\n                check_key(\"foo\")\n                check_key(\"bar\")\n                check_key(\"baz\")\n\n                dogs:delete(\"bar\")\n                safe_set_key(\"blah\", string.rep(\"a\", 8100))\n                dogs:delete(\"foo\")\n                safe_set_key(\"blah\", string.rep(\"a\", 8100))\n                check_key(\"baz\")\n                dogs:delete(\"baz\")\n                check_key(\"blah\")\n            end\n\n            collectgarbage()\n        ';\n    }\n--- request\nGET /test\n--- stap\nglobal first_time = 1\nglobal active = 1\n\nF(ngx_http_lua_shdict_init_zone) {\n    active = 0\n}\n\nF(ngx_http_lua_shdict_init_zone).return {\n    active = 1\n}\n\nF(ngx_slab_alloc_pages) {\n    if (first_time) {\n        printf(\"total pages: %d\\n\", $pool->pages->slab)\n        first_time = 0\n    }\n    if (active) {\n        printf(\"alloc pages: %d\", $pages)\n        //print_ubacktrace()\n    } else {\n        printf(\"init zone alloc pages: %d\", $pages)\n    }\n}\n\nF(ngx_slab_alloc_pages).return {\n    if ($return) {\n        printf(\" ok\\n\")\n\n    } else {\n        printf(\" NOT OK\\n\")\n    }\n}\n\nF(ngx_slab_free_pages) {\n    printf(\"free pages: %d\\n\", $pages)\n}\n\n--- stap_out\ntotal pages: 5\ninit zone alloc pages: 1 ok\ninit zone alloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 1 ok\nfree pages: 1\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 ok\nfree pages: 1\n\n--- response_body\nsuccessfully set foo.\nsuccessfully set bar.\nsuccessfully set baz.\nfound foo: 4000\nfound bar: 4001\nfound baz: 4002\nfailed to safe set blah: no memory\nsuccessfully safe set blah\nfound baz: 4002\nfound blah: 8100\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: merge 2 multi-page blocks (forcibly evicted, merge backward)\n--- http_config\n    lua_shared_dict dogs 30k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            local function check_key(key)\n                local res, err = dogs:get(key)\n                if res then\n                    ngx.say(\"found \", key, \": \", #res)\n                else\n                    if not err then\n                        ngx.say(key, \" not found\")\n                    else\n                        ngx.say(\"failed to fetch key: \", err)\n                    end\n                end\n            end\n\n            local function set_key(key, value)\n                local ok, err, force = dogs:set(key, value)\n                if ok then\n                    ngx.print(\"successfully set \", key)\n                    if force then\n                        ngx.say(\" with force.\")\n                    else\n                        ngx.say(\".\")\n                    end\n                else\n                    ngx.say(\"failed to set \", key, \": \", err)\n                end\n            end\n\n            local function safe_set_key(key, value)\n                local ok, err = dogs:safe_set(key, value)\n                if ok then\n                    ngx.say(\"successfully safe set \", key)\n                else\n                    ngx.say(\"failed to safe set \", key, \": \", err)\n                end\n            end\n\n            for i = 1, 1 do\n                set_key(\"foo\", string.rep(\"a\", 8100))\n                set_key(\"bar\", string.rep(\"b\", 8101))\n                check_key(\"foo\")\n                safe_set_key(\"baz\", string.rep(\"c\", 16300))\n                dogs:delete(\"foo\")\n                check_key(\"bar\")\n                dogs:delete(\"bar\")\n                safe_set_key(\"baz\", string.rep(\"c\", 16300))\n\n                check_key(\"foo\")\n                check_key(\"bar\")\n                check_key(\"baz\")\n            end\n\n            collectgarbage()\n        ';\n    }\n--- request\nGET /test\n--- stap\nglobal first_time = 1\nglobal active = 1\n\nF(ngx_http_lua_shdict_init_zone) {\n    active = 0\n}\n\nF(ngx_http_lua_shdict_init_zone).return {\n    active = 1\n}\n\nF(ngx_slab_alloc_pages) {\n    if (first_time) {\n        printf(\"total pages: %d\\n\", $pool->pages->slab)\n        first_time = 0\n    }\n    if (active) {\n        printf(\"alloc pages: %d\", $pages)\n        //print_ubacktrace()\n    } else {\n        printf(\"init zone alloc pages: %d\", $pages)\n    }\n}\n\nF(ngx_slab_alloc_pages).return {\n    if ($return) {\n        printf(\" ok\\n\")\n\n    } else {\n        printf(\" NOT OK\\n\")\n    }\n}\n\nF(ngx_slab_free_pages) {\n    printf(\"free pages: %d\\n\", $pages)\n}\n\n--- stap_out\ntotal pages: 6\ninit zone alloc pages: 1 ok\ninit zone alloc pages: 1 ok\nalloc pages: 2 ok\nalloc pages: 2 ok\nalloc pages: 4 NOT OK\nfree pages: 2\nfree pages: 2\nalloc pages: 4 ok\n\n--- response_body\nsuccessfully set foo.\nsuccessfully set bar.\nfound foo: 8100\nfailed to safe set baz: no memory\nfound bar: 8101\nsuccessfully safe set baz\nfoo not found\nbar not found\nfound baz: 16300\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: merge big slabs (less than max slab size) backward\n--- http_config\n    lua_shared_dict dogs 20k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            local function check_key(key)\n                local res, err = dogs:get(key)\n                if res then\n                    ngx.say(\"found \", key, \": \", #res)\n                else\n                    if not err then\n                        ngx.say(key, \" not found\")\n                    else\n                        ngx.say(\"failed to fetch key: \", err)\n                    end\n                end\n            end\n\n            local function set_key(key, value)\n                local ok, err, force = dogs:set(key, value)\n                if ok then\n                    ngx.print(\"successfully set \", key)\n                    if force then\n                        ngx.say(\" with force.\")\n                    else\n                        ngx.say(\".\")\n                    end\n                else\n                    ngx.say(\"failed to set \", key, \": \", err)\n                end\n            end\n\n            local function safe_set_key(key, value)\n                local ok, err = dogs:safe_set(key, value)\n                if ok then\n                    ngx.say(\"successfully safe set \", key)\n                else\n                    ngx.say(\"failed to safe set \", key, \": \", err)\n                end\n            end\n\n            for i = 1, 1 do\n                for j = 1, 50 do\n                    dogs:set(\"foo\" .. j, string.rep(\"a\", 5))\n                end\n                set_key(\"bar\", string.rep(\"a\", 4000))\n\n                for j = 1, 50 do\n                    dogs:delete(\"foo\" .. j)\n                end\n\n                safe_set_key(\"baz\", string.rep(\"b\", 8100))\n                check_key(\"bar\")\n\n                ngx.say(\"delete bar\")\n                dogs:delete(\"bar\")\n\n                safe_set_key(\"baz\", string.rep(\"b\", 8100))\n            end\n\n            collectgarbage()\n        ';\n    }\n--- request\nGET /test\n--- stap\nglobal first_time = 1\nglobal active = 1\n\nF(ngx_http_lua_shdict_init_zone) {\n    active = 0\n}\n\nF(ngx_http_lua_shdict_init_zone).return {\n    active = 1\n}\n\nF(ngx_slab_alloc_pages) {\n    if (first_time) {\n        //printf(\"slab max size: %d\\n\", @var(\"ngx_slab_max_size\"))\n        printf(\"total pages: %d\\n\", $pool->pages->slab)\n        first_time = 0\n    }\n    if (active) {\n        printf(\"alloc pages: %d\", $pages)\n        //print_ubacktrace()\n    } else {\n        printf(\"init zone alloc pages: %d\", $pages)\n    }\n}\n\nF(ngx_slab_alloc_pages).return {\n    if ($return) {\n        printf(\" ok\\n\")\n\n    } else {\n        printf(\" NOT OK\\n\")\n    }\n}\n\nF(ngx_slab_free_pages) {\n    printf(\"free pages: %d\\n\", $pages)\n}\n\n--- stap_out\ntotal pages: 4\ninit zone alloc pages: 1 ok\ninit zone alloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 1 ok\nfree pages: 1\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 ok\n\n--- response_body\nsuccessfully set bar.\nfailed to safe set baz: no memory\nfound bar: 4000\ndelete bar\nsuccessfully safe set baz\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: cannot merge in-used big slabs page (backward)\n--- http_config\n    lua_shared_dict dogs 20k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            local function check_key(key)\n                local res, err = dogs:get(key)\n                if res then\n                    ngx.say(\"found \", key, \": \", #res)\n                else\n                    if not err then\n                        ngx.say(key, \" not found\")\n                    else\n                        ngx.say(\"failed to fetch key: \", err)\n                    end\n                end\n            end\n\n            local function set_key(key, value)\n                local ok, err, force = dogs:set(key, value)\n                if ok then\n                    ngx.print(\"successfully set \", key)\n                    if force then\n                        ngx.say(\" with force.\")\n                    else\n                        ngx.say(\".\")\n                    end\n                else\n                    ngx.say(\"failed to set \", key, \": \", err)\n                end\n            end\n\n            local function safe_set_key(key, value)\n                local ok, err = dogs:safe_set(key, value)\n                if ok then\n                    ngx.say(\"successfully safe set \", key)\n                else\n                    ngx.say(\"failed to safe set \", key, \": \", err)\n                end\n            end\n\n            for i = 1, 1 do\n                for j = 1, 63 do\n                    dogs:set(\"foo\" .. j, string.rep(\"a\", 5))\n                end\n                set_key(\"bar\", string.rep(\"a\", 4000))\n\n                --[[\n                for j = 1, 50 do\n                    dogs:delete(\"foo\" .. j)\n                end\n                ]]\n\n                safe_set_key(\"baz\", string.rep(\"b\", 8100))\n                check_key(\"bar\")\n\n                ngx.say(\"delete bar\")\n                dogs:delete(\"bar\")\n\n                safe_set_key(\"baz\", string.rep(\"b\", 8100))\n            end\n\n            collectgarbage()\n        ';\n    }\n--- request\nGET /test\n--- stap\nglobal first_time = 1\nglobal active = 1\n\nF(ngx_http_lua_shdict_init_zone) {\n    active = 0\n}\n\nF(ngx_http_lua_shdict_init_zone).return {\n    active = 1\n}\n\nF(ngx_slab_alloc_pages) {\n    if (first_time) {\n        //printf(\"slab max size: %d\\n\", @var(\"ngx_slab_max_size\"))\n        printf(\"total pages: %d\\n\", $pool->pages->slab)\n        first_time = 0\n    }\n    if (active) {\n        printf(\"alloc pages: %d\", $pages)\n        //print_ubacktrace()\n    } else {\n        printf(\"init zone alloc pages: %d\", $pages)\n    }\n}\n\nF(ngx_slab_alloc_pages).return {\n    if ($return) {\n        printf(\" ok\\n\")\n\n    } else {\n        printf(\" NOT OK\\n\")\n    }\n}\n\nF(ngx_slab_free_pages) {\n    printf(\"free pages: %d\\n\", $pages)\n}\n\n--- stap_out\ntotal pages: 4\ninit zone alloc pages: 1 ok\ninit zone alloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 NOT OK\n\n--- response_body\nsuccessfully set bar.\nfailed to safe set baz: no memory\nfound bar: 4000\ndelete bar\nfailed to safe set baz: no memory\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: cannot merge in-used big slabs page (forward)\n--- http_config\n    lua_shared_dict dogs 20k;\n--- config\n    location = /test {\n        content_by_lua '\n            local dogs = ngx.shared.dogs\n\n            local function check_key(key)\n                local res, err = dogs:get(key)\n                if res then\n                    ngx.say(\"found \", key, \": \", #res)\n                else\n                    if not err then\n                        ngx.say(key, \" not found\")\n                    else\n                        ngx.say(\"failed to fetch key: \", err)\n                    end\n                end\n            end\n\n            local function set_key(key, value)\n                local ok, err, force = dogs:set(key, value)\n                if ok then\n                    ngx.print(\"successfully set \", key)\n                    if force then\n                        ngx.say(\" with force.\")\n                    else\n                        ngx.say(\".\")\n                    end\n                else\n                    ngx.say(\"failed to set \", key, \": \", err)\n                end\n            end\n\n            local function safe_set_key(key, value)\n                local ok, err = dogs:safe_set(key, value)\n                if ok then\n                    ngx.say(\"successfully safe set \", key)\n                else\n                    ngx.say(\"failed to safe set \", key, \": \", err)\n                end\n            end\n\n            for i = 1, 1 do\n                set_key(\"bar\", string.rep(\"a\", 4000))\n                for j = 1, 50 do\n                    dogs:set(\"foo\" .. j, string.rep(\"a\", 5))\n                end\n\n                --[[\n                for j = 1, 50 do\n                    dogs:delete(\"foo\" .. j)\n                end\n                ]]\n\n                safe_set_key(\"baz\", string.rep(\"b\", 8100))\n                check_key(\"bar\")\n\n                ngx.say(\"delete bar\")\n                dogs:delete(\"bar\")\n\n                safe_set_key(\"baz\", string.rep(\"b\", 8100))\n            end\n\n            collectgarbage()\n        ';\n    }\n--- request\nGET /test\n--- stap\nglobal first_time = 1\nglobal active = 1\n\nF(ngx_http_lua_shdict_init_zone) {\n    active = 0\n}\n\nF(ngx_http_lua_shdict_init_zone).return {\n    active = 1\n}\n\nF(ngx_slab_alloc_pages) {\n    if (first_time) {\n        //printf(\"slab max size: %d\\n\", @var(\"ngx_slab_max_size\"))\n        printf(\"total pages: %d\\n\", $pool->pages->slab)\n        first_time = 0\n    }\n    if (active) {\n        printf(\"alloc pages: %d\", $pages)\n        //print_ubacktrace()\n    } else {\n        printf(\"init zone alloc pages: %d\", $pages)\n    }\n}\n\nF(ngx_slab_alloc_pages).return {\n    if ($return) {\n        printf(\" ok\\n\")\n\n    } else {\n        printf(\" NOT OK\\n\")\n    }\n}\n\nF(ngx_slab_free_pages) {\n    printf(\"free pages: %d\\n\", $pages)\n}\n\n--- stap_out\ntotal pages: 4\ninit zone alloc pages: 1 ok\ninit zone alloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 1 ok\nalloc pages: 2 NOT OK\nfree pages: 1\nalloc pages: 2 NOT OK\n\n--- response_body\nsuccessfully set bar.\nfailed to safe set baz: no memory\nfound bar: 4000\ndelete bar\nfailed to safe set baz: no memory\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: fuzz testing\n--- http_config\n    lua_shared_dict dogs 200k;\n--- config\n    location = /t {\n        content_by_lua '\n            local rand = math.random\n            local dogs = ngx.shared.dogs\n            local maxsz = 9000\n            local maxkeyidx = 30\n            local rep = string.rep\n\n            math.randomseed(ngx.time())\n            for i = 1, 30000 do\n                local key = \"mylittlekey\" .. rand(maxkeyidx)\n                local ok, err = dogs:get(key)\n                if not ok or rand() > 0.6 then\n                    local sz = rand(maxsz)\n                    local val = rep(\"a\", sz)\n                    local ok, err, forcible = dogs:set(key, val)\n                    if err then\n                        ngx.log(ngx.ERR, \"failed to set key: \", err)\n                        -- return\n                    end\n                    if forcible then\n                        -- error(\"forcible\")\n                    end\n                end\n            end\n            ngx.say(\"ok\")\n            collectgarbage()\n        ';\n    }\n--- request\nGET /t\n--- response_body\nok\n\n--- no_error_log\n[error]\n--- timeout: 60\n"
  },
  {
    "path": "t/127-uthread-kill.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = $t::StapThread::GCScript;\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 5 + 1) - 4;\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';\n$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';\n\n#no_shuffle();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: kill pending sleep\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello from f()\")\n                ngx.sleep(1)\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            collectgarbage()\n\n            local ok, err = ngx.thread.kill(t)\n            if not ok then\n                ngx.say(\"failed to kill thread: \", err)\n                return\n            end\n\n            ngx.say(\"killed\")\n\n            local ok, err = ngx.thread.kill(t)\n            if not ok then\n                ngx.say(\"failed to kill thread: \", err)\n                return\n            end\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nhello from f()\nthread created: running\nkilled\nfailed to kill thread: already waited or killed\n\n--- no_error_log\n[error]\n--- error_log\nlua clean up the timer for pending ngx.sleep\n\n\n\n=== TEST 2: already waited\n--- config\n    location /lua {\n        content_by_lua '\n            local function f()\n                ngx.say(\"hello from f()\")\n                ngx.sleep(0.001)\n                return 32\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            collectgarbage()\n\n            local ok, res = ngx.thread.wait(t)\n            if not ok then\n                ngx.say(\"failed to kill thread: \", res)\n                return\n            end\n\n            ngx.say(\"waited: \", res)\n\n            local ok, err = ngx.thread.kill(t)\n            if not ok then\n                ngx.say(\"failed to kill thread: \", err)\n                return\n            end\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\nterminate 2: ok\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nhello from f()\nthread created: running\nwaited: 32\nfailed to kill thread: already waited or killed\n\n--- no_error_log\n[error]\nlua clean up the timer for pending ngx.sleep\n\n\n\n=== TEST 3: kill pending resolver\n--- config\n    resolver 127.0.0.2:12345;\n    resolver_timeout 5ms;\n    location /lua {\n        content_by_lua '\n            local function f()\n                local sock = ngx.socket.tcp()\n                sock:connect(\"some.127.0.0.2\", 12345)\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            collectgarbage()\n\n            local ok, err = ngx.thread.kill(t)\n            if not ok then\n                ngx.say(\"failed to kill thread: \", err)\n                return\n            end\n\n            ngx.say(\"killed\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nthread created: running\nkilled\n\n--- no_error_log\n[error]\n--- error_log\nlua tcp socket abort resolver\n\n\n\n=== TEST 4: kill pending connect\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /lua {\n        content_by_lua '\n            local ready = false\n            local function f()\n                local sock = ngx.socket.tcp()\n                sock:connect(\"agentzh.org\", 80)\n                sock:close()\n                ready = true\n                sock:settimeout(10000)\n                sock:connect(\"127.0.0.2\", 12345)\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            collectgarbage()\n\n            while not ready do\n                ngx.sleep(0.001)\n            end\n\n            local ok, err = ngx.thread.kill(t)\n            if not ok then\n                ngx.say(\"failed to kill thread: \", err)\n                return\n            end\n\n            ngx.say(\"killed\")\n        ';\n    }\n--- request\nGET /lua\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\ncreate 2 in 1\nspawn user thread 2 in 1\ndelete thread 2\nterminate 1: ok\ndelete thread 1\n\n--- response_body\nthread created: running\nkilled\n\n--- no_error_log\n[error]\nlua tcp socket abort resolver\n--- grep_error_log: lua finalize socket\n--- grep_error_log_out\nlua finalize socket\nlua finalize socket\n\n--- error_log\n\n\n\n=== TEST 5: cannot kill a pending subrequest\n--- config\n    location = /sub {\n        echo_sleep 0.3;\n        echo ok;\n    }\n\n    location = /t {\n        content_by_lua '\n            local function f()\n                ngx.location.capture(\"/sub\")\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            collectgarbage()\n\n            local ok, err = ngx.thread.kill(t)\n            if not ok then\n                ngx.say(\"failed to kill thread: \", err)\n                return\n            end\n\n            ngx.say(\"killed\")\n        ';\n    }\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- response_body\nthread created: running\nfailed to kill thread: pending subrequests\n\n--- no_error_log\n[error]\n[alert]\nlua tcp socket abort resolver\n--- error_log\n\n\n\n=== TEST 6: cannot kill a pending subrequest not in the thread being killed\n--- config\n    location = /sub {\n        echo_sleep 0.3;\n        echo ok;\n    }\n\n    location = /t {\n        content_by_lua '\n            local function f()\n                ngx.location.capture(\"/sub\")\n            end\n\n            local function g()\n                ngx.sleep(0.3)\n            end\n\n            local tf, err = ngx.thread.spawn(f)\n            if not tf then\n                ngx.say(\"failed to spawn thread 1: \", err)\n                return\n            end\n\n            ngx.say(\"thread f created: \", coroutine.status(tf))\n\n            local tg, err = ngx.thread.spawn(g)\n            if not tg then\n                ngx.say(\"failed to spawn thread g: \", err)\n                return\n            end\n\n            ngx.say(\"thread g created: \", coroutine.status(tg))\n\n            collectgarbage()\n\n            local ok, err = ngx.thread.kill(tf)\n            if not ok then\n                ngx.say(\"failed to kill thread f: \", err)\n            else\n                ngx.say(\"killed f\")\n            end\n\n            local ok, err = ngx.thread.kill(tg)\n            if not ok then\n                ngx.say(\"failed to kill thread g: \", err)\n            else\n                ngx.say(\"killed g\")\n            end\n        ';\n    }\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- response_body\nthread f created: running\nthread g created: running\nfailed to kill thread f: pending subrequests\nkilled g\n\n--- no_error_log\n[error]\n[alert]\nlua tcp socket abort resolver\n--- error_log\n\n\n\n=== TEST 7: kill a thread that has done a subrequest but no pending ones\n--- config\n    location = /sub {\n        echo ok;\n    }\n\n    location = /t {\n        content_by_lua '\n            local ready = false\n            local function f()\n                ngx.location.capture(\"/sub\")\n                ready = true\n                ngx.sleep(0.5)\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            collectgarbage()\n\n            while not ready do\n                ngx.sleep(0.001)\n            end\n\n            local ok, err = ngx.thread.kill(t)\n            if not ok then\n                ngx.say(\"failed to kill thread: \", err)\n                return\n            end\n\n            ngx.say(\"killed\")\n        ';\n    }\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- response_body\nthread created: running\nkilled\n\n--- no_error_log\n[error]\n[alert]\nlua tcp socket abort resolver\n--- error_log\n\n\n\n=== TEST 8: kill a thread already terminated\n--- config\n    location = /t {\n        content_by_lua '\n            local function f()\n                return\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n\n            collectgarbage()\n\n            local ok, err = ngx.thread.kill(t)\n            if not ok then\n                ngx.say(\"failed to kill thread: \", err)\n                return\n            end\n\n            ngx.say(\"killed\")\n        ';\n    }\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- response_body\nthread created: zombie\nfailed to kill thread: already terminated\n\n--- no_error_log\n[error]\n[alert]\nlua tcp socket abort resolver\n--- error_log\n\n\n\n=== TEST 9: kill self\n--- config\n    location = /t {\n        content_by_lua '\n            local ok, err = ngx.thread.kill(coroutine.running())\n            if not ok then\n                ngx.say(\"failed to kill main thread: \", err)\n            else\n                ngx.say(\"killed main thread.\")\n            end\n\n            local function f()\n                local ok, err = ngx.thread.kill(coroutine.running())\n                if not ok then\n                    ngx.say(\"failed to kill user thread: \", err)\n                else\n                    ngx.say(\"user thread thread.\")\n                end\n\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.say(\"failed to spawn thread: \", err)\n                return\n            end\n\n            ngx.say(\"thread created: \", coroutine.status(t))\n        ';\n    }\n--- request\nGET /t\n--- stap2 eval: $::StapScript\n--- response_body\nfailed to kill main thread: not user thread\nfailed to kill user thread: killer not parent\nthread created: zombie\n\n--- no_error_log\n[error]\n[alert]\nlua tcp socket abort resolver\n--- error_log\n\n\n\n=== TEST 10: phantom uthreads-- via killed uthread with live child coroutine\nWhen a user thread internally uses coroutine.resume() to run a child\ncoroutine that does cosocket I/O, and the parent uthread is then killed\nvia ngx.thread.kill(), the child's socket I/O is NOT cancelled (because\ncleanup_pending_operation on the parent's co_ctx is a no-op -- the parent\nyielded for coroutine.resume, not for I/O, so cleanup is NULL).\n\nWhen the child later completes, user_co_done walks up to the killed\nparent's co_ctx, finds is_uthread=1, and does uthreads-- even though\ndel_thread returns early (co_ref already LUA_NOREF). This phantom\ndecrement causes uthreads to underflow when finalize_threads runs\nduring the access->content phase transition.\n--- config\n    location = /t {\n        rewrite_by_lua_block {\n            ngx.ctx._rewrite = true\n        }\n\n        access_by_lua_block {\n            local redis_port = $TEST_NGINX_REDIS_PORT\n\n            -- Phase 1: simulate DNS resolution with thread.spawn + wait + kill\n            -- (like thread_process_dns_query in resolver.lua)\n            local dns_threads = {}\n\n            -- T1: fast DNS query (PING, completes immediately)\n            dns_threads[1] = ngx.thread.spawn(function()\n                local sock = ngx.socket.tcp()\n                sock:settimeout(2000)\n                local ok, err = sock:connect(\"127.0.0.1\", redis_port)\n                if not ok then return nil, err end\n                sock:send(\"PING\\r\\n\")\n                local line = sock:receive()\n                sock:setkeepalive()\n                return line\n            end)\n\n            -- T2: uses coroutine.resume internally with FAST I/O.\n            -- The child coroutine does a fast Redis PING so its response\n            -- arrives in the same epoll batch as T1's response.\n            -- When T2 is killed, the child's socket is NOT cleaned up\n            -- because T2 yielded for coroutine.resume (cleanup=NULL),\n            -- not for socket I/O. The child's event then fires AFTER\n            -- T2 is killed but BEFORE finalize_threads runs.\n            dns_threads[2] = ngx.thread.spawn(function()\n                local child = coroutine.create(function()\n                    local sock = ngx.socket.tcp()\n                    sock:settimeout(2000)\n                    local ok, err = sock:connect(\"127.0.0.1\", redis_port)\n                    if not ok then return nil, err end\n                    sock:send(\"PING\\r\\n\")\n                    local line = sock:receive()\n                    sock:setkeepalive()\n                    return line\n                end)\n\n                local ok, res = coroutine.resume(child)\n                return res\n            end)\n\n            -- wait_any pattern: wait for first result\n            local ok, res = ngx.thread.wait(dns_threads[1], dns_threads[2])\n\n            -- kill remaining (T2's child socket is still alive!)\n            for _, t in ipairs(dns_threads) do\n                ngx.thread.kill(t)\n            end\n\n            -- Phase 2: simulate probe thread spawning\n            -- (like run_batch_probe_targets in edge_probe)\n            local probe_threads = {}\n            for i = 1, 10 do\n                probe_threads[i] = ngx.thread.spawn(function(idx)\n                    local sock = ngx.socket.tcp()\n                    sock:settimeout(2000)\n                    local ok, err = sock:connect(\"127.0.0.1\", redis_port)\n                    if not ok then return nil, err end\n                    sock:send(\"PING\\r\\n\")\n                    local line = sock:receive()\n                    sock:setkeepalive()\n                    return line\n                end, i)\n            end\n\n            local ok_count = 0\n            for i = 1, #probe_threads do\n                local ok, res, err = ngx.thread.wait(probe_threads[i])\n                if ok and res then\n                    ok_count = ok_count + 1\n                end\n            end\n\n            ngx.ctx.ok_count = ok_count\n        }\n\n        content_by_lua_block {\n            -- reset_ctx -> finalize_threads runs here.\n            -- Without the fix, the phantom uthreads-- from the killed T2's\n            -- child completing causes uthreads underflow.\n            ngx.say(\"ok_count=\", ngx.ctx.ok_count)\n        }\n    }\n--- request\nGET /t\n--- response_body\nok_count=10\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/128-duplex-tcp-socket.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 2);\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: pipelined memcached requests (sent one byte at a time)\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"flush_all\\\\r\\\\nget foo\\\\r\\\\nget bar\\\\r\\\\n\"\n            -- req = \"OK\"\n            local send_idx = 1\n\n            local function writer()\n                local sub = string.sub\n                while send_idx <= #req do\n                    local bytes, err = sock:send(sub(req, send_idx, send_idx))\n                    if not bytes then\n                        ngx.say(\"failed to send request: \", err)\n                        return\n                    end\n                    -- if send_idx % 2 == 0 then\n                        ngx.sleep(0.001)\n                    -- end\n                    send_idx = send_idx + 1\n                end\n                -- ngx.say(\"request sent.\")\n            end\n\n            local ok, err = ngx.thread.spawn(writer)\n            if not ok then\n                ngx.say(\"failed to spawn writer thread: \", err)\n                return\n            end\n\n            for i = 1, 3 do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:setkeepalive()\n            ngx.say(\"setkeepalive: \", ok, \" \", err)\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nreceived: OK\nreceived: END\nreceived: END\nsetkeepalive: 1 nil\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: read timeout errors won't affect writing\n--- config\n    server_tokens off;\n    lua_socket_log_errors off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"flush_all\\\\r\\\\n\"\n            -- req = \"OK\"\n            local send_idx = 1\n\n            sock:settimeout(1)\n\n            local function writer()\n                local sub = string.sub\n                while send_idx <= #req do\n                    local bytes, err = sock:send(sub(req, send_idx, send_idx))\n                    if not bytes then\n                        ngx.say(\"failed to send request: \", err)\n                        return\n                    end\n                    ngx.sleep(0.001)\n                    send_idx = send_idx + 1\n                end\n                -- ngx.say(\"request sent.\")\n            end\n\n            local ok, err = ngx.thread.spawn(writer)\n            if not ok then\n                ngx.say(\"failed to spawn writer thread: \", err)\n                return\n            end\n\n            local data = \"\"\n            local ntm = 0\n            local done = false\n            for i = 1, 300 do\n                local line, err, part = sock:receive()\n                if not line then\n                    if part then\n                        data = data .. part\n                    end\n                    if err ~= \"timeout\" then\n                        ngx.say(\"failed to receive: \", err)\n                        return\n                    end\n\n                    ntm = ntm + 1\n\n                else\n                    data = data .. line\n                    ngx.say(\"received: \", data)\n                    done = true\n                    break\n                end\n            end\n\n            if not done then\n                ngx.say(\"partial read: \", data)\n            end\n\n            ngx.say(\"read timed out: \", ntm)\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n--- request\nGET /t\n--- response_body_like chop\n^connected: 1\n(?:received: OK|failed to send request: timeout\npartial read: )\nread timed out: [1-9]\\d*\nclose: 1 nil$\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: writes are rejected while reads are not\n--- config\n    server_tokens off;\n    lua_socket_log_errors off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_RAND_PORT_1;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"flush_all\\\\r\\\\n\"\n            -- req = \"OK\"\n            local send_idx = 1\n\n            local function writer()\n                local sub = string.sub\n                while send_idx <= #req do\n                    local bytes, err = sock:send(sub(req, send_idx, send_idx))\n                    if not bytes then\n                        ngx.say(\"failed to send request: \", err)\n                        return\n                    end\n                    ngx.sleep(0.001)\n                    send_idx = send_idx + 1\n                end\n                -- ngx.say(\"request sent.\")\n            end\n\n            local ok, err = ngx.thread.spawn(writer)\n            if not ok then\n                ngx.say(\"failed to spawn writer thread: \", err)\n                return\n            end\n\n            local data = \"\"\n            local ntm = 0\n            local done = false\n            for i = 1, 3 do\n                local res, err, part = sock:receive(1)\n                if not res then\n                    ngx.say(\"failed to receive: \", err)\n                    return\n                else\n                    data = data .. res\n                end\n                ngx.sleep(0.001)\n            end\n\n            ngx.say(\"received: \", data)\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n--- request\nGET /t\n--- response_body_like chop\n^connected: 1\nreceived: OK!\nclose: (?:nil socket busy writing|1 nil\nfailed to send request: closed)$\n\n--- tcp_listen: $TEST_NGINX_RAND_PORT_1\n--- tcp_shutdown: 0\n--- tcp_reply: OK!\n--- tcp_no_close: 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: reads are rejected while writes are not\n--- config\n    server_tokens off;\n    lua_socket_log_errors off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_RAND_PORT_1;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local req = \"flush_all\\\\r\\\\n\"\n            -- req = \"OK\"\n            local send_idx = 1\n\n            local function writer()\n                local sub = string.sub\n                while send_idx <= #req do\n                    local bytes, err = sock:send(sub(req, send_idx, send_idx))\n                    if not bytes then\n                        ngx.say(\"failed to send request: \", err)\n                        return\n                    end\n                    -- ngx.say(\"sent: \", bytes)\n                    ngx.sleep(0.001)\n                    send_idx = send_idx + 1\n                end\n                ngx.say(\"request sent.\")\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end\n\n            local ok, err = ngx.thread.spawn(writer)\n            if not ok then\n                ngx.say(\"failed to spawn writer thread: \", err)\n                return\n            end\n\n            local data = \"\"\n            local ntm = 0\n            local aborted = false\n            for i = 1, 3 do\n                if not aborted then\n                    local res, err, part = sock:receive(1)\n                    if not res then\n                        ngx.say(\"failed to receive: \", err)\n                        aborted = true\n                    else\n                        data = data .. res\n                    end\n                end\n\n                ngx.sleep(0.001)\n            end\n\n            if not aborted then\n                ngx.say(\"received: \", data)\n            end\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: closed\nrequest sent.\nclose: 1 nil\n\n--- stap2\nF(ngx_http_lua_socket_tcp_finalize_write_part) {\n    print_ubacktrace()\n}\n--- stap_out2\n--- tcp_listen: $TEST_NGINX_RAND_PORT_1\n--- tcp_shutdown: 1\n--- tcp_query eval: \"flush_all\\r\\n\"\n--- tcp_query_len: 11\n--- no_error_log\n[error]\n--- wait: 0.05\n\n\n\n=== TEST 5: concurrent socket operations while connecting\n--- config\n    server_tokens off;\n    lua_socket_log_errors off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n\n            local function f()\n                ngx.sleep(0.001)\n                local res, err = sock:receive(1)\n                ngx.say(\"receive: \", res, \" \", err)\n\n                local bytes, err = sock:send(\"hello\")\n                ngx.say(\"send: \", bytes, \" \", err)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n\n                local ok, err = sock:getreusedtimes()\n                ngx.say(\"getreusedtimes: \", ok, \" \", err)\n\n                local ok, err = sock:setkeepalive()\n                ngx.say(\"setkeepalive: \", ok, \" \", err)\n\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n                ngx.say(\"connect: \", ok, \" \", err)\n            end\n\n            local ok, err = ngx.thread.spawn(f)\n            if not ok then\n                ngx.say(\"failed to spawn writer thread: \", err)\n                return\n            end\n\n            sock:settimeout(300)\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            ngx.say(\"connect: \", ok, \" \", err)\n\n            local ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nreceive: nil socket busy connecting\nsend: nil socket busy connecting\nclose: nil socket busy connecting\ngetreusedtimes: 0 nil\nsetkeepalive: nil socket busy connecting\nconnect: nil socket busy connecting\nconnect: nil timeout\nclose: nil closed\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: concurrent operations while resolving\n--- config\n    server_tokens off;\n    lua_socket_log_errors off;\n    resolver 127.0.0.2:12345;\n    resolver_timeout 300ms;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n\n            local function f()\n                ngx.sleep(0.001)\n                local res, err = sock:receive(1)\n                ngx.say(\"receive: \", res, \" \", err)\n\n                local bytes, err = sock:send(\"hello\")\n                ngx.say(\"send: \", bytes, \" \", err)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n\n                local ok, err = sock:getreusedtimes()\n                ngx.say(\"getreusedtimes: \", ok, \" \", err)\n\n                local ok, err = sock:setkeepalive()\n                ngx.say(\"setkeepalive: \", ok, \" \", err)\n\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n                ngx.say(\"connect: \", ok, \" \", err)\n            end\n\n            local ok, err = ngx.thread.spawn(f)\n            if not ok then\n                ngx.say(\"failed to spawn writer thread: \", err)\n                return\n            end\n\n            sock:settimeout(300)\n            local ok, err = sock:connect(\"some2.agentzh.org\", 80)\n            ngx.say(\"connect: \", ok, \" \", err)\n\n            local ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nreceive: nil closed\nsend: nil closed\nclose: nil closed\ngetreusedtimes: nil closed\nsetkeepalive: nil closed\nconnect: nil socket busy connecting\nconnect: nil some2.agentzh.org could not be resolved (110: Operation timed out)\nclose: nil closed\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: concurrent operations while reading (receive)\n--- config\n    server_tokens off;\n    lua_socket_log_errors off;\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            local ready = false\n\n            local function f()\n                while not ready do\n                    ngx.sleep(0.001)\n                end\n\n                local res, err = sock:receive(1)\n                ngx.say(\"receive: \", res, \" \", err)\n\n                local bytes, err = sock:send(\"flush_all\")\n                ngx.say(\"send: \", bytes, \" \", err)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n\n                local ok, err = sock:getreusedtimes()\n                ngx.say(\"getreusedtimes: \", ok, \" \", err)\n\n                local ok, err = sock:setkeepalive()\n                ngx.say(\"setkeepalive: \", ok, \" \", err)\n\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n                ngx.say(\"connect: \", ok, \" \", err)\n            end\n\n            local ok, err = ngx.thread.spawn(f)\n            if not ok then\n                ngx.say(\"failed to spawn writer thread: \", err)\n                return\n            end\n\n            sock:settimeout(300)\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            ngx.say(\"connect: \", ok, \" \", err)\n\n            ready = true\n\n            local res, err = sock:receive(1)\n            ngx.say(\"receive: \", res, \" \", err)\n\n            local ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnect: 1 nil\nreceive: nil socket busy reading\nsend: 9 nil\nclose: nil socket busy reading\ngetreusedtimes: 0 nil\nsetkeepalive: nil socket busy reading\nconnect: nil socket busy reading\nreceive: nil timeout\nclose: 1 nil\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: concurrent operations while reading (receiveuntil)\n--- config\n    server_tokens off;\n    lua_socket_log_errors off;\n    location /t {\n        content_by_lua '\n            local ready = false\n            local sock = ngx.socket.tcp()\n\n            local function f()\n                while not ready do\n                    ngx.sleep(0.001)\n                end\n\n                local res, err = sock:receive(1)\n                ngx.say(\"receive: \", res, \" \", err)\n\n                local bytes, err = sock:send(\"flush_all\")\n                ngx.say(\"send: \", bytes, \" \", err)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n\n                local ok, err = sock:getreusedtimes()\n                ngx.say(\"getreusedtimes: \", ok, \" \", err)\n\n                local ok, err = sock:setkeepalive()\n                ngx.say(\"setkeepalive: \", ok, \" \", err)\n\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n                ngx.say(\"connect: \", ok, \" \", err)\n            end\n\n            local ok, err = ngx.thread.spawn(f)\n            if not ok then\n                ngx.say(\"failed to spawn writer thread: \", err)\n                return\n            end\n\n            sock:settimeout(300)\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            ngx.say(\"connect: \", ok, \" \", err)\n\n            ready = true\n\n            local it, err = sock:receiveuntil(\"\\\\r\\\\n\")\n            if not it then\n                ngx.say(\"receiveuntil() failed: \", err)\n                return\n            end\n\n            local res, err = it()\n            ngx.say(\"receiveuntil() iterator: \", res, \" \", err)\n\n            local ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnect: 1 nil\nreceive: nil socket busy reading\nsend: 9 nil\nclose: nil socket busy reading\ngetreusedtimes: 0 nil\nsetkeepalive: nil socket busy reading\nconnect: nil socket busy reading\nreceiveuntil() iterator: nil timeout\nclose: 1 nil\n\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/129-ssl-socket.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\nBEGIN {\n    if (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n        # FIXME: we still need to enable this test file for HTTP3.\n        $SkipReason = \"the test cases are very unstable, skip for now.\";\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\nuse Cwd qw(abs_path realpath);\nuse File::Basename;\n\nrepeat_each(2);\n\nsub resolve($$);\n\nplan tests => repeat_each() * (blocks() * 7 - 4);\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_SERVER_SSL_PORT} ||= 12345;\n$ENV{TEST_NGINX_CERT_DIR} ||= dirname(realpath(abs_path(__FILE__)));\n$ENV{TEST_NGINX_OPENRESTY_ORG_IP} ||= resolve(\"openresty.org\", $ENV{TEST_NGINX_RESOLVER});\n\nmy $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';\nmy $openssl_version = eval { `$NginxBinary -V 2>&1` };\nif ($openssl_version =~ m/BoringSSL/) {\n    $ENV{TEST_NGINX_USE_BORINGSSL} = 1;\n}\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\n\nsub read_file {\n    my $infile = shift;\n    open my $in, $infile\n        or die \"cannot open $infile for reading: $!\";\n    my $cert = do { local $/; <$in> };\n    close $in;\n    $cert;\n}\n\nsub resolve ($$) {\n    my ($domain, $resolver) = @_;\n    my $ips = qx/dig \\@$resolver +short $domain/;\n\n    my $exit_code = $? >> 8;\n    if (!$ips || $exit_code != 0) {\n        die \"failed to resolve '$domain' using '$resolver' as resolver\";\n    }\n\n    my ($ip) = split /\\n/, $ips;\n    return $ip;\n}\n\nour $DSTRootCertificate = read_file(\"t/cert/dst-ca.crt\");\nour $EquifaxRootCertificate = read_file(\"t/cert/equifax.crt\");\nour $TestCertificate = read_file(\"t/cert/test.crt\");\nour $TestCertificateKey = read_file(\"t/cert/test.key\");\nour $TestCRL = read_file(\"t/cert/test.crl\");\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: www.google.com\naccess the public network is unstable, need a bigger timeout value.\n--- quic_max_idle_timeout: 3\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            -- avoid flushing bing in \"check leak\" testing mode:\n            local counter = package.loaded.counter\n            if not counter then\n                counter = 1\n            elseif counter >= 2 then\n                return ngx.exit(503)\n            else\n                counter = counter + 1\n            end\n            package.loaded.counter = counter\n\n            do\n                local sock = ngx.socket.tcp()\n                sock:settimeout(2000)\n                local ok, err = sock:connect(\"www.google.com\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake()\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET / HTTP/1.1\\\\r\\\\nHost: www.google.com\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body_like chop\n\\Aconnected: 1\nssl handshake: cdata\nsent http request: 59 bytes.\nreceived: HTTP/1.1 (?:200 OK|302 Found)\nclose: 1 nil\n\\z\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- no_error_log\nlua ssl server name:\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 2: no SNI, no verify\n--- http_config\n    server {\n        listen $TEST_NGINX_SERVER_SSL_PORT ssl;\n        server_name   test.com;\n        ssl_certificate ../html/test.crt;\n        ssl_certificate_key ../html/test.key;\n\n        location / {\n            content_by_lua_block {\n                ngx.exit(201)\n            }\n        }\n    }\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n                sock:settimeout(2000)\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_SERVER_SSL_PORT)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake()\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET / HTTP/1.1\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 53 bytes.\nreceived: HTTP/1.1 201 Created\nclose: 1 nil\n--- user_files eval\n\">>> test.key\n$::TestCertificateKey\n>>> test.crt\n$::TestCertificate\"\n\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- no_error_log\nlua ssl server name:\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 3: SNI, no verify\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"openresty.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"openresty.org\")\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET / HTTP/1.1\\r\\nHost: openresty.org\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 58 bytes.\nreceived: HTTP/1.1 302 Moved Temporarily\nclose: 1 nil\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- error_log\nlua ssl server name: \"openresty.org\"\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 4: ssl session reuse\naccess the public network is unstable, need a bigger timeout value.\n--- quic_max_idle_timeout: 3\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_protocols TLSv1.2;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(7000)\n\n            do\n\n            local session\n            for i = 1, 2 do\n                local ok, err = sock:connect(\"agentzh.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                session, err = sock:sslhandshake(session, \"agentzh.org\")\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET / HTTP/1.1\\\\r\\\\nHost: agentzh.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end\n\n            end -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 200 OK\nclose: 1 nil\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 200 OK\nclose: 1 nil\n\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl set session: \\1\nlua ssl save session: \\1\nlua ssl free session: \\1\nlua ssl free session: \\1\n$/\n\n--- error_log\nSSL reused session\nlua ssl free session\n\n--- log_level: debug\n--- no_error_log\n[error]\n[alert]\n--- timeout: 10\n\n\n\n=== TEST 5: certificate does not match host name (verify)\nThe certificate of \"openresty.org\" does not contain the name \"blah.openresty.org\".\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate ../html/trusted.crt;\n    lua_ssl_verify_depth 5;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"openresty.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"blah.openresty.org\", true)\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                else\n                    ngx.say(\"ssl handshake: \", type(session))\n                end\n\n                local req = \"GET / HTTP/1.1\\\\r\\\\nHost: openresty.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- user_files eval\n\">>> trusted.crt\n$::DSTRootCertificate\"\n\n--- request\nGET /t\n--- response_body_like chomp\n\\Aconnected: 1\nfailed to do SSL handshake: (?:handshake failed|certificate host mismatch)\nfailed to send http request: closed\n\\z\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- error_log\nlua ssl server name: \"blah.openresty.org\"\n--- no_error_log\nSSL reused session\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 6: certificate does not match host name (verify, no log socket errors)\nThe certificate for \"openresty.org\" does not contain the name \"blah.openresty.org\".\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate ../html/trusted.crt;\n    lua_socket_log_errors off;\n    lua_ssl_verify_depth 2;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"openresty.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"blah.openresty.org\", true)\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                else\n                    ngx.say(\"ssl handshake: \", type(session))\n                end\n\n                local req = \"GET / HTTP/1.1\\\\r\\\\nHost: blah.openresty.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- user_files eval\n\">>> trusted.crt\n$::DSTRootCertificate\"\n\n--- request\nGET /t\n--- response_body_like chomp\n\\Aconnected: 1\nfailed to do SSL handshake: (?:handshake failed|certificate host mismatch)\nfailed to send http request: closed\n\\z\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- error_log\nlua ssl server name: \"blah.openresty.org\"\n--- no_error_log\nlua ssl certificate does not match host\nSSL reused session\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 7: certificate does not match host name (no verify)\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(4000)\n\n            do\n                local ok, err = sock:connect(\"openresty.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"openresty.org\", false)\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET /en/linux-packages.html HTTP/1.1\\\\r\\\\nHost: openresty.com\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 80 bytes.\nreceived: HTTP/1.1 404 Not Found\nclose: 1 nil\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n\n--- error_log\nlua ssl server name: \"openresty.org\"\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 8: openresty.org: passing SSL verify\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate ../html/trusted.crt;\n    lua_ssl_verify_depth 2;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(4000)\n\n            do\n                local ok, err = sock:connect(\"openresty.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"openresty.org\", true)\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET / HTTP/1.1\\\\r\\\\nHost: openresty.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- user_files eval\n\">>> trusted.crt\n$::DSTRootCertificate\"\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 58 bytes.\nreceived: HTTP/1.1 302 Moved Temporarily\nclose: 1 nil\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]++/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n\n--- error_log\nlua ssl server name: \"openresty.org\"\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 9: ssl verify depth not enough (with automatic error logging)\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate ../html/trusted.crt;\n    lua_ssl_verify_depth 0;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"openresty.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"openresty.org\", true)\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                else\n                    ngx.say(\"ssl handshake: \", type(session))\n                end\n\n                local req = \"GET / HTTP/1.1\\\\r\\\\nHost: openresty.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- user_files eval\n\">>> trusted.crt\n$::DSTRootCertificate\"\n\n--- request\nGET /t\n--- response_body eval\nqr{connected: 1\nfailed to do SSL handshake: (22: certificate chain too long|20: unable to get local issuer certificate|21: unable to verify the first certificate)\nfailed to send http request: closed\n}\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- error_log eval\n['lua ssl server name: \"openresty.org\"',\nqr/lua ssl certificate verify error: \\((22: certificate chain too long|20: unable to get local issuer certificate|21: unable to verify the first certificate)\\)/]\n--- no_error_log\nSSL reused session\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 10: ssl verify depth not enough (without automatic error logging)\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate ../html/trusted.crt;\n    lua_ssl_verify_depth 0;\n    lua_socket_log_errors off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"openresty.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"openresty.org\", true)\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                else\n                    ngx.say(\"ssl handshake: \", type(session))\n                end\n\n                local req = \"GET / HTTP/1.1\\\\r\\\\nHost: openresty.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- user_files eval\n\">>> trusted.crt\n$::DSTRootCertificate\"\n\n--- request\nGET /t\n--- response_body eval\nqr/connected: 1\nfailed to do SSL handshake: (22: certificate chain too long|20: unable to get local issuer certificate|21: unable to verify the first certificate)\nfailed to send http request: closed\n/\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- error_log\nlua ssl server name: \"openresty.org\"\n--- no_error_log\nlua ssl certificate verify error\nSSL reused session\n[alert]\n--- timeout: 7\n\n\n\n=== TEST 11: openresty.org: SSL verify enabled and no corresponding trusted certificates\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate ../html/trusted.crt;\n    lua_ssl_verify_depth 2;\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(4000)\n\n            do\n                local ok, err = sock:connect(\"openresty.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"openresty.org\", true)\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET / HTTP/1.1\\r\\nHost: openresty.org\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        }\n    }\n\n--- user_files eval\n\">>> trusted.crt\n$::EquifaxRootCertificate\"\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: 20: unable to get local issuer certificate\n\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- error_log\nlua ssl server name: \"openresty.org\"\nlua ssl certificate verify error: (20: unable to get local issuer certificate)\n--- no_error_log\nSSL reused session\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 12: openresty.org: passing SSL verify with multiple certificates\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate ../html/trusted.crt;\n    lua_ssl_verify_depth 2;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(4000)\n\n            do\n                local ok, err = sock:connect(\"openresty.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"openresty.org\", true)\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET / HTTP/1.1\\\\r\\\\nHost: openresty.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- user_files eval\n\">>> trusted.crt\n$::EquifaxRootCertificate\n$::DSTRootCertificate\"\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 58 bytes.\nreceived: HTTP/1.1 302 Moved Temporarily\nclose: 1 nil\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n\n--- error_log\nlua ssl server name: \"openresty.org\"\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 13: default cipher\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"openresty.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"openresty.org\")\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET / HTTP/1.1\\\\r\\\\nHost: openresty.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 58 bytes.\nreceived: HTTP/1.1 302 Moved Temporarily\nclose: 1 nil\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- error_log eval\n[\n'lua ssl server name: \"openresty.org\"',\nqr/SSL: TLSv1\\.2, cipher: \"(?:ECDHE-RSA-AES(?:256|128)-GCM-SHA(?:384|256)|ECDHE-(?:RSA|ECDSA)-CHACHA20-POLY1305) TLSv1\\.2/,\n]\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 14: explicit cipher configuration\n--- http_config\n    server {\n        listen              unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name         test.com;\n        ssl_certificate     $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_protocols       TLSv1.2;\n\n        location / {\n            content_by_lua_block {\n                ngx.exit(200)\n            }\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384;\n\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"test.com\")\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET / HTTP/1.1\\\\r\\\\nHost: test.com\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 53 bytes.\nreceived: HTTP/1.1 200 OK\nclose: 1 nil\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- error_log eval\n['lua ssl server name: \"test.com\"',\nqr/SSL: TLSv\\d(?:\\.\\d)?, cipher: \"ECDHE-RSA-AES256-GCM-SHA384 (SSLv3|TLSv1\\.2)/]\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 10\n\n\n\n=== TEST 15: explicit ssl protocol configuration\n--- http_config\n    server {\n        listen              unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name         test.com;\n        ssl_certificate     $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_protocols       TLSv1.2;\n\n        location / {\n            content_by_lua_block {\n                ngx.exit(200)\n            }\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_protocols TLSv1.2;\n\n    location /t {\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"test.com\")\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET / HTTP/1.1\\\\r\\\\nHost: test.com\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 53 bytes.\nreceived: HTTP/1.1 200 OK\nclose: 1 nil\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- error_log eval\n['lua ssl server name: \"test.com\"',\nqr/SSL: TLSv1\\.2, cipher: \"ECDHE-RSA-AES256-GCM-SHA384 TLSv1\\.2/]\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n\n\n\n=== TEST 16: unsupported ssl protocol\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_protocols SSLv2;\n    lua_socket_log_errors off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"openresty.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"openresty.org\")\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                else\n                    ngx.say(\"ssl handshake: \", type(session))\n                end\n\n                local req = \"GET / HTTP/1.1\\\\r\\\\nHost: openresty.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\nfailed to send http request: closed\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- error_log eval\n[\nqr/\\[(crit|error)\\] .*?SSL_do_handshake\\(\\) failed .*?(unsupported protocol|no protocols available)/,\n'lua ssl server name: \"openresty.org\"',\n]\n--- no_error_log\nSSL reused session\n[alert]\n[emerg]\n--- timeout: 5\n\n\n\n=== TEST 17: openresty.org: passing SSL verify: keepalive (reuse the ssl session)\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate ../html/trusted.crt;\n    lua_ssl_verify_depth 2;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n        set $openresty_org_ip $TEST_NGINX_OPENRESTY_ORG_IP;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n\n            local session\n            for i = 1, 3 do\n                -- Use the same IP to ensure that the connection can be reused\n                local ok, err = sock:connect(ngx.var.openresty_org_ip, 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                session, err = sock:sslhandshake(session, \"openresty.org\", true)\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local ok, err = sock:setkeepalive()\n                ngx.say(\"set keepalive: \", ok, \" \", err)\n            end  -- do\n\n            end\n            collectgarbage()\n        ';\n    }\n\n--- user_files eval\n\">>> trusted.crt\n$::DSTRootCertificate\"\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nset keepalive: 1 nil\nconnected: 1\nssl handshake: cdata\nset keepalive: 1 nil\nconnected: 1\nssl handshake: cdata\nset keepalive: 1 nil\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: \\1\n$/\n\n--- error_log\nlua tcp socket get keepalive peer: using connection\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 18: openresty.org: passing SSL verify: keepalive (no reusing the ssl session)\nThe session returned by SSL_get1_session maybe different.\nAfter function tls_process_new_session_ticket, the session saved in SSL->session\nwill be replace by a new one.\n\nngx_ssl_session_t *\nngx_ssl_get_session(ngx_connection_t *c)\n{\n#ifdef TLS1_3_VERSION\n    if (c->ssl->session) {\n        SSL_SESSION_up_ref(c->ssl->session);\n        return c->ssl->session;\n    }\n#endif\n\n    return SSL_get1_session(c->ssl->connection);\n}\n\nSSL_SESSION *SSL_get1_session(SSL *ssl)\n/* variant of SSL_get_session: caller really gets something */\n{\n    SSL_SESSION *sess;\n    /*\n     * Need to lock this all up rather than just use CRYPTO_add so that\n     * somebody doesn't free ssl->session between when we check it's non-null\n     * and when we up the reference count.\n     */\n    CRYPTO_THREAD_read_lock(ssl->lock);\n    sess = ssl->session;\n    if (sess)\n        SSL_SESSION_up_ref(sess);\n    CRYPTO_THREAD_unlock(ssl->lock);\n    return sess;\n}\n\n#0  tls_process_new_session_ticket (s=0x7e6ea0, pkt=0x7fffffffc820) at ssl/statem/statem_clnt.c:2650\n#1  0x00007ffff7af50fd in read_state_machine (s=0x7e6ea0) at ssl/statem/statem.c:636\n#2  state_machine (s=0x7e6ea0, server=0) at ssl/statem/statem.c:434\n#3  0x00007ffff7aca6b3 in ssl3_read_bytes (s=<optimized out>, type=23, recvd_type=0x0, buf=0x7fffffffc9d7 \"\\027\\320\\355t\", len=1, \n    peek=0, readbytes=0x7fffffffc978) at ssl/record/rec_layer_s3.c:1677\n#4  0x00007ffff7ad2250 in ssl3_read_internal (readbytes=0x7fffffffc978, peek=0, len=1, buf=0x7fffffffc9d7, s=0x7e6ea0)\n    at ssl/s3_lib.c:4477\n#5  ssl3_read (s=0x7e6ea0, buf=0x7fffffffc9d7, len=1, readbytes=0x7fffffffc978) at ssl/s3_lib.c:4500\n#6  0x00007ffff7ade695 in SSL_read (s=<optimized out>, buf=buf@entry=0x7fffffffc9d7, num=num@entry=1) at ssl/ssl_lib.c:1799\n#7  0x000000000045a965 in ngx_ssl_recv (c=0x72c3b0, buf=0x7fffffffc9d7 \"\\027\\320\\355t\", size=1)\n    at src/event/ngx_event_openssl.c:2337\n#8  0x0000000000533b17 in ngx_http_lua_socket_keepalive_close_handler (ev=0x7e2f20)\n    at /var/code/openresty/lua-nginx-module/src/ngx_http_lua_socket_tcp.c:5753\n#9  0x000000000052cf40 in ngx_http_lua_socket_tcp_setkeepalive (L=0x74edd0)\n    at /var/code/openresty/lua-nginx-module/src/ngx_http_lua_socket_tcp.c:5602\n#10 0x00007ffff7f0fabe in lj_BC_FUNCC ()\n   from /tmp/undodb.72729.1722915526.2470007.80d50d088e818fd4/debuggee-1-zwqz8svp/symbol-files/opt/luajit-sysm/lib/libluajit-5.1.so.2\n#11 0x000000000051f2b2 in ngx_http_lua_run_thread (L=L@entry=0x767670, r=r@entry=0x7edf80, ctx=ctx@entry=0x750e40, nrets=0)\n    at /var/code/openresty/lua-nginx-module/src/ngx_http_lua_util.c:1194\n#12 0x0000000000524347 in ngx_http_lua_content_by_chunk (L=0x767670, r=0x7edf80)\n    at /var/code/openresty/lua-nginx-module/src/ngx_http_lua_contentby.c:124\n#13 0x000000000047c663 in ngx_http_core_content_phase (r=0x7edf80, ph=0x7b4470) at src/http/ngx_http_core_module.c:1271\n#14 0x000000000047b80d in ngx_http_core_run_phases (r=0x7edf80) at src/http/ngx_http_core_module.c:885\n#15 ngx_http_handler (r=r@entry=0x7edf80) at src/http/ngx_http_core_module.c:868\n#16 0x00000000004854ad in ngx_http_process_request (r=r@entry=0x7edf80) at src/http/ngx_http_request.c:2140\n#17 0x00000000004868e8 in ngx_http_process_request_headers (rev=rev@entry=0x7e2f80) at src/http/ngx_http_request.c:1529\n#18 0x0000000000486468 in ngx_http_process_request_line (rev=0x7e2f80) at src/http/ngx_http_request.c:1196\n#19 0x000000000044b338 in ngx_event_process_posted (cycle=cycle@entry=0x721690, posted=0x62f250 <ngx_posted_events>)\n    at src/event/ngx_event_posted.c:35\n#20 0x000000000044a522 in ngx_process_events_and_timers (cycle=cycle@entry=0x721690) at src/event/ngx_event.c:273\n#21 0x0000000000453819 in ngx_single_process_cycle (cycle=cycle@entry=0x721690) at src/os/unix/ngx_process_cycle.c:323\n#22 0x0000000000429dee in main (argc=argc@entry=5, argv=argv@entry=0x7fffffffd1a8) at src/core/nginx.c:384\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate ../html/trusted.crt;\n    lua_ssl_verify_depth 2;\n    location /t {\n        #set $port 5000;\n        set $openresty_org_ip $TEST_NGINX_OPENRESTY_ORG_IP;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n\n            for i = 1, 3 do\n                -- Use the same IP to ensure that the connection can be reused\n                local ok, err = sock:connect(ngx.var.openresty_org_ip, 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"openresty.org\", true)\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local ok, err = sock:setkeepalive()\n                ngx.say(\"set keepalive: \", ok, \" \", err)\n            end  -- do\n\n            end\n            collectgarbage()\n        ';\n    }\n\n--- user_files eval\n\">>> trusted.crt\n$::DSTRootCertificate\"\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nset keepalive: 1 nil\nconnected: 1\nssl handshake: cdata\nset keepalive: 1 nil\nconnected: 1\nssl handshake: cdata\nset keepalive: 1 nil\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl save session: ([0-9A-F]+)\nlua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n\n--- error_log\nlua tcp socket get keepalive peer: using connection\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 19: downstream cosockets do not support ssl handshake\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate ../html/trusted.crt;\n    lua_ssl_verify_depth 2;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.req.socket()\n            local sess, err = sock:sslhandshake()\n            if not sess then\n                ngx.say(\"failed to do ssl handshake: \", err)\n            else\n                ngx.say(\"ssl handshake: \", type(sess))\n            end\n        ';\n    }\n\n--- user_files eval\n\">>> trusted.crt\n$::DSTRootCertificate\"\n\n--- request\nPOST /t\nhello world\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- error_log\nattempt to call method 'sslhandshake' (a nil value)\n--- no_error_log\n[alert]\n--- timeout: 3\n--- skip_eval: 5:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 20: unix domain ssl cosocket (no verify)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate ../html/test.crt;\n        ssl_certificate_key ../html/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua 'ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            do\n                local sock = ngx.socket.tcp()\n                sock:settimeout(3000)\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake()\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: test.com\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- user_files eval\n\">>> test.key\n$::TestCertificateKey\n>>> test.crt\n$::TestCertificate\"\n\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- no_error_log\nlua ssl server name:\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 21: unix domain ssl cosocket (verify)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate ../html/test.crt;\n        ssl_certificate_key ../html/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua 'ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate ../html/test.crt;\n\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            do\n                local sock = ngx.socket.tcp()\n                sock:settimeout(3000)\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: test.com\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- user_files eval\n\">>> test.key\n$::TestCertificateKey\n>>> test.crt\n$::TestCertificate\"\n\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- error_log\nlua ssl server name: \"test.com\"\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 22: unix domain ssl cosocket (no ssl on server)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        server_name   test.com;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua 'ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake()\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: test.com\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- user_files eval\n\">>> test.crt\n$::TestCertificate\"\n\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- error_log eval\nqr/SSL_do_handshake\\(\\) failed .*?(unknown protocol|wrong version number|routines::record layer failure)/\n--- no_error_log\nlua ssl server name:\nSSL reused session\n[alert]\n--- timeout: 3\n\n\n\n=== TEST 23: lua_ssl_crl\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate ../html/test.crt;\n        ssl_certificate_key ../html/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua 'ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_crl ../html/test.crl;\n    lua_ssl_trusted_certificate ../html/test.crt;\n    lua_socket_log_errors off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(3000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                else\n                    ngx.say(\"ssl handshake: \", type(sess))\n                end\n\n                local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: test.com\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body eval\n# Since nginx version 1.19.1, invalidity date is considered a non-critical CRL\n# entry extension, in other words, revoke still works even if CRL has expired.\n$Test::Nginx::Util::NginxVersion >= 1.019001 ?\n\n\"connected: 1\nfailed to do SSL handshake: 23: certificate revoked\nfailed to send http request: closed\\n\" :\n\n\"connected: 1\nfailed to do SSL handshake: 12: CRL has expired\nfailed to send http request: closed\\n\";\n\n--- user_files eval\n\">>> test.key\n$::TestCertificateKey\n>>> test.crt\n$::TestCertificate\n>>> test.crl\n$::TestCRL\"\n\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- error_log\nlua ssl server name: \"test.com\"\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 24: multiple handshake calls\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"openresty.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                for i = 1, 2 do\n                    local session, err = sock:sslhandshake(nil, \"openresty.org\")\n                    if not session then\n                        ngx.say(\"failed to do SSL handshake: \", err)\n                        return\n                    end\n\n                    ngx.say(\"ssl handshake: \", type(session))\n                end\n\n                local req = \"GET / HTTP/1.1\\\\r\\\\nHost: openresty.org\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nssl handshake: cdata\nsent http request: 58 bytes.\nreceived: HTTP/1.1 302 Moved Temporarily\nclose: 1 nil\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- error_log\nlua ssl server name: \"openresty.org\"\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 25: handshake timed out\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"openresty.org\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                sock:settimeout(1);  -- should timeout immediately\n                local session, err = sock:sslhandshake(nil, \"openresty.org\")\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: timeout\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 26: unix domain ssl cosocket (no gen session)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate ../html/test.crt;\n        ssl_certificate_key ../html/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua 'ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            do\n                local sock = ngx.socket.tcp()\n                sock:settimeout(3000)\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", sess)\n\n                sock:close()\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: true\n\n--- user_files eval\n\">>> test.key\n$::TestCertificateKey\n>>> test.crt\n$::TestCertificate\"\n\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- no_error_log\nlua ssl server name:\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 27: unix domain ssl cosocket (gen session, true)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate ../html/test.crt;\n        ssl_certificate_key ../html/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua 'ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            do\n                local sock = ngx.socket.tcp()\n                sock:settimeout(3000)\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                sock:close()\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\n\n--- user_files eval\n\">>> test.key\n$::TestCertificateKey\n>>> test.crt\n$::TestCertificate\"\n\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- no_error_log\nlua ssl server name:\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 28: unix domain ssl cosocket (keepalive)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate ../html/test.crt;\n        ssl_certificate_key ../html/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua 'ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local sock = ngx.socket.tcp()\n            sock:settimeout(3000)\n            for i = 1, 2 do\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", sess)\n\n                local ok, err = sock:setkeepalive()\n                if not ok then\n                    ngx.say(\"failed to set keepalive: \", err)\n                    return\n                end\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: true\nconnected: 1\nssl handshake: true\n\n--- user_files eval\n\">>> test.key\n$::TestCertificateKey\n>>> test.crt\n$::TestCertificate\"\n\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- no_error_log\nlua ssl server name:\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 29: unix domain ssl cosocket (verify cert but no host name check, passed)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate ../html/test.crt;\n        ssl_certificate_key ../html/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua 'ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate ../html/test.crt;\n\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            do\n                local sock = ngx.socket.tcp()\n                sock:settimeout(3000)\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, nil, true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: test.com\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- user_files eval\n\">>> test.key\n$::TestCertificateKey\n>>> test.crt\n$::TestCertificate\"\n\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- error_log\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 30: unix domain ssl cosocket (verify cert but no host name check, NOT passed)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate ../html/test.crt;\n        ssl_certificate_key ../html/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua 'ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)';\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    #lua_ssl_trusted_certificate ../html/test.crt;\n\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            do\n                local sock = ngx.socket.tcp()\n                sock:settimeout(3000)\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, nil, true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\\\r\\\\nHost: test.com\\\\r\\\\nConnection: close\\\\r\\\\n\\\\r\\\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        ';\n    }\n\n--- request\nGET /t\n--- response_body eval\nqr/connected: 1\nfailed to do SSL handshake: 18: self[- ]signed certificate\n/ms\n--- user_files eval\n\">>> test.key\n$::TestCertificateKey\n>>> test.crt\n$::TestCertificate\"\n\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- error_log eval\nqr/lua ssl certificate verify error: \\(18: self[- ]signed certificate\\)/\n--- no_error_log\nSSL reused session\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 31: handshake, too few arguments\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(7000)\n\n            local ok, err = sock:connect(\"openresty.org\", 443)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local session, err = sock.sslhandshake()\n        }\n    }\n\n--- request\nGET /t\n--- ignore_response\n--- error_log eval\nqr/\\[error\\] .* ngx.socket sslhandshake: expecting 1 ~ 5 arguments \\(including the object\\), but seen 0/\n--- no_error_log\n[alert]\n--- timeout: 10\n--- curl_error eval\nqr#curl: \\(52\\) Empty reply from server|curl: \\(95\\) HTTP/3 stream 0 reset by server#\n\n\n\n=== TEST 32: default cipher -TLSv1.3\n--- skip_openssl: 8: < 1.1.1\n--- http_config\n    server {\n        listen              unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name         test.com;\n        ssl_certificate     $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_protocols       TLSv1.3;\n\n        location / {\n            content_by_lua_block {\n                ngx.exit(200)\n            }\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_protocols TLSv1.3;\n\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"test.com\")\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET / HTTP/1.1\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 53 bytes.\nreceived: HTTP/1.1 200 OK\nclose: 1 nil\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- error_log eval\n['lua ssl server name: \"test.com\"',\nqr/SSL: TLSv1.3, cipher: \"TLS_AES_256_GCM_SHA384 TLSv1.3/]\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 10\n\n\n\n=== TEST 33: explicit cipher configuration - TLSv1.3\n--- skip_openssl: 8: < 1.1.1\n--- skip_nginx: 8: < 1.19.4\n--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}\n--- http_config\n    server {\n        listen              unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name         test.com;\n        ssl_certificate     $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_protocols       TLSv1.3;\n\n        location / {\n            content_by_lua_block {\n                ngx.exit(200)\n            }\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_protocols TLSv1.3;\n    lua_ssl_conf_command Ciphersuites TLS_AES_128_GCM_SHA256;\n\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"test.com\")\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET / HTTP/1.1\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 53 bytes.\nreceived: HTTP/1.1 200 OK\nclose: 1 nil\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- error_log eval\n['lua ssl server name: \"test.com\"',\nqr/SSL: TLSv1.3, cipher: \"TLS_AES_128_GCM_SHA256 TLSv1.3/]\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 10\n\n\n\n=== TEST 34: explicit cipher configuration not in the default list - TLSv1.3\n--- skip_openssl: 8: < 1.1.1\n--- skip_nginx: 8: < 1.19.4\n--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}\n--- http_config\n    server {\n        listen              unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name         test.com;\n        ssl_certificate     $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_protocols       TLSv1.3;\n\n        location / {\n            content_by_lua_block {\n                ngx.exit(200)\n            }\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_protocols TLSv1.3;\n    lua_ssl_conf_command Ciphersuites TLS_AES_128_CCM_SHA256;\n\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"test.com\")\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET / HTTP/1.1\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- log_level: debug\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out\n--- error_log eval\n[\nqr/\\[info\\] .*?SSL_do_handshake\\(\\) failed .*?no shared cipher/,\n'lua ssl server name: \"test.com\"',\n]\n--- no_error_log\nSSL reused session\n[alert]\n[emerg]\n--- timeout: 10\n\n\n\n=== TEST 35: lua_ssl_key_log directive\n--- skip_openssl: 8: < 1.1.1\n--- http_config\n    server {\n        listen              $TEST_NGINX_SERVER_SSL_PORT ssl;\n        server_name         test.com;\n        ssl_certificate     $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_protocols       TLSv1.3;\n\n        location / {\n            content_by_lua_block {\n                ngx.exit(200)\n            }\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_protocols TLSv1.3;\n    lua_ssl_key_log sslkey.log;\n\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n\n            do\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_SERVER_SSL_PORT)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local session, err = sock:sslhandshake(nil, \"test.com\")\n                if not session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(session))\n\n                local req = \"GET / HTTP/1.1\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n\n                local f, err = io.open(\"$TEST_NGINX_SERVER_ROOT/conf/sslkey.log\", \"r\")\n                if not f then\n                    ngx.log(ngx.ERR, \"failed to open sslkey.log: \", err)\n                    return\n                end\n\n                local key_log = f:read(\"*a\")\n                ngx.say(key_log)\n                f:close()\n            end  -- do\n            collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body_like\nconnected: 1\nssl handshake: cdata\nsent http request: 53 bytes.\nreceived: HTTP/1.1 200 OK\nclose: 1 nil\nSERVER_HANDSHAKE_TRAFFIC_SECRET [0-9a-z\\s]+\nEXPORTER_SECRET [0-9a-z\\s]+\nSERVER_TRAFFIC_SECRET_0 [0-9a-z\\s]+\nCLIENT_HANDSHAKE_TRAFFIC_SECRET [0-9a-z\\s]+\nCLIENT_TRAFFIC_SECRET_0 [0-9a-z\\s]+\n\n--- log_level: debug\n--- error_log eval\n['lua ssl server name: \"test.com\"',\nqr/SSL: TLSv1.3, cipher: \"TLS_AES_256_GCM_SHA384 TLSv1.3/]\n--- no_error_log\nSSL reused session\n[error]\n[alert]\n--- timeout: 10\n"
  },
  {
    "path": "t/130-internal-api.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * blocks() * 3;\n\n#no_diff();\nno_long_string();\n#master_on();\n#workers(2);\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: req\n--- config\n    location = /t {\n        content_by_lua '\n            local ffi = require \"ffi\"\n            local function tonum(ud)\n                return tonumber(ffi.cast(\"uintptr_t\", ud))\n            end\n            ngx.say(string.format(\"content req=%#x\", tonum(exdata())))\n        ';\n    }\n--- request\nGET /t\n\n--- response_body_like chop\n^content req=0x[a-f0-9]{4,}\n$\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/131-duplex-req-socket.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if (!defined $ENV{LD_PRELOAD}) {\n        $ENV{LD_PRELOAD} = '';\n    }\n\n    if ($ENV{LD_PRELOAD} !~ /\\bmockeagain\\.so\\b/) {\n        $ENV{LD_PRELOAD} = \"mockeagain.so $ENV{LD_PRELOAD}\";\n    }\n\n    if ($ENV{MOCKEAGAIN} eq 'r') {\n        $ENV{MOCKEAGAIN} = 'rw';\n\n    } else {\n        $ENV{MOCKEAGAIN} = 'w';\n    }\n\n    $ENV{TEST_NGINX_EVENT_TYPE} = 'poll';\n    $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'slow';\n\n    if ($ENV{TEST_NGINX_USE_HTTP3}) {\n        $SkipReason = \"http3 does not support ngx.req.socket()\";\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\nlog_level('debug');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2);\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: raw downstream cosocket used in two different threads. See issue #481\n--- config\n    lua_socket_read_timeout 1ms;\n    lua_socket_send_timeout 1s;\n    lua_socket_log_errors off;\n\n    location /t {\n        content_by_lua '\n            local function reader(req_socket)\n               -- First we receive in a blocking fashion so that ctx->downstream_co_ctx will be changed\n               local data, err, partial = req_socket:receive(1)\n               if err ~= \"timeout\" then\n                  ngx.log(ngx.ERR, \"Did not get timeout in the receiving thread!\")\n                  return\n               end\n\n               -- Now, sleep so that coctx->data is changed to sleep handler\n               ngx.sleep(1)\n            end\n\n            local function writer(req_socket)\n               -- send in a slow manner with a low timeout, so that the timeout handler will be\n               local bytes, err = req_socket:send(\"slow!!!\")\n               if err ~= \"timeout\" then\n                  return error(\"Did not get timeout in the sending thread!\")\n               end\n            end\n\n            local req_socket, err = ngx.req.socket(true)\n            if req_socket == nil then\n               ngx.status = 500\n               return error(\"Unable to get request socket:\" .. (err or \"nil\"))\n            end\n\n            local writer_thread = ngx.thread.spawn(writer, req_socket)\n            local reader_thread = ngx.thread.spawn(reader, req_socket)\n\n            ngx.thread.wait(writer_thread)\n            ngx.thread.wait(reader_thread)\n            print(\"The two threads finished\")\n';\n        }\n--- request\nPOST /t\n--- more_headers\nContent-Length: 1\n--- no_error_log\n[error]\n--- error_log: The two threads finished\n--- wait: 0.1\n--- ignore_response\n--- timeout: 10\n\n\n\n=== TEST 2: normal downstream cosocket used in two different threads. See issue #481\n--- config\n    lua_socket_read_timeout 1ms;\n    lua_socket_send_timeout 1s;\n    lua_socket_log_errors off;\n    send_timeout 1s;\n\n    location /t {\n        content_by_lua '\n            local function reader(req_socket)\n               -- First we receive in a blocking fashion so that ctx->downstream_co_ctx will be changed\n               local data, err, partial = req_socket:receive(1)\n               if err ~= \"timeout\" then\n                  ngx.log(ngx.ERR, \"Did not get timeout in the receiving thread!\")\n                  return\n               end\n\n               -- Now, sleep so that coctx->data is changed to sleep handler\n               ngx.sleep(1)\n            end\n\n            local function writer(req_socket)\n               -- send in a slow manner with a low timeout, so that the timeout handler will be\n               ngx.sleep(0.3)\n               ngx.say(\"slow!!!\")\n               ngx.flush(true)\n            end\n\n            local req_socket, err = ngx.req.socket()\n            if req_socket == nil then\n               ngx.status = 500\n               return error(\"Unable to get request socket:\" .. (err or \"nil\"))\n            end\n\n            local writer_thread = ngx.thread.spawn(writer, req_socket)\n            local reader_thread = ngx.thread.spawn(reader, req_socket)\n\n            ngx.thread.wait(writer_thread)\n            ngx.thread.wait(reader_thread)\n            print(\"The two threads finished\")\n';\n        }\n--- request\nPOST /t\n--- more_headers\nContent-Length: 1\n--- no_error_log\n[error]\n--- error_log: The two threads finished\n--- wait: 0.1\n--- ignore_response\n--- timeout: 10\n"
  },
  {
    "path": "t/132-lua-blocks.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3 + 3);\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: content_by_lua_block (simplest)\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.say(\"hello, world\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nhello, world\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: content_by_lua_block (nested curly braces)\n--- config\n    location = /t {\n        content_by_lua_block {\n            local a = {\n                dogs = {32, 78, 96},\n                cat = \"kitty\",\n            }\n            ngx.say(\"a.dogs[1] = \", a.dogs[1])\n            ngx.say(\"a.dogs[2] = \", a.dogs[2])\n            ngx.say(\"a.dogs[3] = \", a.dogs[3])\n            ngx.say(\"a.cat = \", a.cat)\n        }\n    }\n--- request\nGET /t\n--- response_body\na.dogs[1] = 32\na.dogs[2] = 78\na.dogs[3] = 96\na.cat = kitty\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: content_by_lua_block (curly braces in strings)\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.say(\"}1, 2)\")\n            ngx.say('{1, 2)')\n        }\n    }\n--- request\nGET /t\n--- response_body\n}1, 2)\n{1, 2)\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: content_by_lua_block (curly braces in strings, with escaped terminators)\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.say(\"\\\"}1, 2)\")\n            ngx.say('\\'{1, 2)')\n        }\n    }\n--- request\nGET /t\n--- response_body\n\"}1, 2)\n'{1, 2)\n\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: content_by_lua_block (curly braces in long brackets)\n--- config\n    location = /t {\n        content_by_lua_block {\n            --[[\n                {{{\n\n                        }\n            ]]\n            --[==[\n                }}}\n\n                        {\n            ]==]\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: content_by_lua_block (\"nested\" long brackets)\n--- config\n    location = /t {\n        content_by_lua_block {\n            --[[\n                ]=]\n            '  \"\n                        }\n            ]]\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: content_by_lua_block (curly braces in line comments)\n--- config\n    location = /t {\n        content_by_lua_block {\n            --}} {}\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: content_by_lua_block (cosockets)\n--- no_http2\n--- config\n    server_tokens off;\n    location = /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect('127.0.0.1', tonumber(ngx.var.server_port))\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say('connected: ', ok)\n\n            local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n            -- req = \"OK\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent: \", bytes)\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n\n    location /foo {\n        content_by_lua_block { ngx.say(\"foo\") }\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 57\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nfailed to receive a line: closed []\nclose: 1 nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: all in one\n--- http_config\n    init_by_lua_block {\n        glob = \"init by lua }here{\"\n    }\n\n    init_worker_by_lua_block {\n        glob = glob .. \", init worker }here{\"\n    }\n--- config\n    location = /t {\n        set $a '';\n        rewrite_by_lua_block {\n            local s = ngx.var.a\n            s = s .. \"}rewrite{\\n\"\n            ngx.var.a = s\n        }\n        access_by_lua_block {\n            local s = ngx.var.a\n            s = s .. '}access{\\n'\n            ngx.var.a = s\n        }\n        content_by_lua_block {\n            local s = ngx.var.a\n            s = s .. [[}content{]]\n            ngx.say(s)\n            ngx.say(\"glob: \", glob)\n        }\n        log_by_lua_block {\n            print(\"log by lua running \\\"}{!\\\"\")\n        }\n        header_filter_by_lua_block {\n            ngx.header[\"Foo\"] = \"\\\"Hello, world\\\"\"\n            ngx.header[\"Content-Length\"] = nil\n        }\n        body_filter_by_lua_block {\n            local data, eof = ngx.arg[1], ngx.arg[2]\n            print(\"eof = \", eof)\n            if eof then\n                if not data then\n                    data = \"\"\n                end\n                data = data .. \"}body filter{\\n\"\n                print(\"data: \", data)\n                ngx.arg[1] = data\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\n}rewrite{\n}access{\n}content{\nglob: init by lua }here{, init worker }here{\n}body filter{\n\n--- response_headers\nFoo: \"Hello, world\"\n--- error_log\nlog by lua running \"}{!\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: missing ]] (string)\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.say([[hello, world\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nhello, world\n--- no_error_log\n[error]\n--- must_die\n--- error_log eval\nqr/\\[emerg\\] .*? Lua code block missing the closing long bracket \"]]\", the inlined Lua code may be too long in .*?\\bnginx\\.conf:\\d+/\n\n\n\n=== TEST 11: missing ]==] (string)\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.say([==[hello, world\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nhello, world\n--- no_error_log\n[error]\n--- must_die\n--- error_log eval\nqr/\\[emerg\\] .*? Lua code block missing the closing long bracket \"]==]\", the inlined Lua code may be too long in .*?\\bnginx.conf:\\d+/\n\n\n\n=== TEST 12: missing ]] (comment)\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.say(--[[hello, world\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nhello, world\n--- no_error_log\n[error]\n--- must_die\n--- error_log eval\nqr/\\[emerg\\] .*? Lua code block missing the closing long bracket \"]]\", the inlined Lua code may be too long in .*?\\bnginx\\.conf:\\d+/\n\n\n\n=== TEST 13: missing ]=] (comment)\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.say(--[=[hello, world\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nhello, world\n--- no_error_log\n[error]\n--- must_die\n--- error_log eval\nqr/\\[emerg\\] .*? Lua code block missing the closing long bracket \"]=]\", the inlined Lua code may be too long in .*?\\bnginx\\.conf:\\d+/\n\n\n\n=== TEST 14: missing }\nFIXME: we need better diagnostics by actually loading the inlined Lua code while parsing\nthe *_by_lua_block directive.\n\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.say(\"hello\")\n--- request\nGET /t\n--- response_body\nhello, world\n--- no_error_log\n[error]\n--- error_log\n\"events\" directive is not allowed here\n--- must_die\n\n\n\n=== TEST 15: content_by_lua_block (compact)\n--- config\n    location = /t {\n        content_by_lua_block {ngx.say(\"hello, world\", {\"!\"})}\n    }\n--- request\nGET /t\n--- response_body\nhello, world!\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: content_by_lua_block unexpected closing long brackets must FAIL\n--- config\n    location = /t {\n        content_by_lua_block {\n            ]=]\n        }\n    }\n--- request\nGET /t\n--- error_code: 500\n--- error_log eval\nqr{\\[error\\] .*? unexpected symbol near ']'}\n\n\n\n=== TEST 17: content_by_lua_block unexpected closing long brackets ignored (GitHub #748)\n--- config\n    location = /t {\n        content_by_lua_block {\n            local t1, t2 = {\"hello world\"}, {1}\n            ngx.say(t1[t2[1]])\n        }\n    }\n--- request\nGET /t\n--- response_body\nhello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: simple set_by_lua_block (integer)\n--- config\n    location /lua {\n        set_by_lua_block $res { return 1+1 }\n        echo $res;\n    }\n--- request\nGET /lua\n--- response_body\n2\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: ambiguous line comments inside a long bracket string (GitHub #596)\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.say([[ok--]])\n            ngx.say([==[ok--]==])\n            ngx.say([==[ok-- ]==])\n            --[[ --]] ngx.say(\"done\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok--\nok--\nok-- \ndone\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: double quotes in long brackets\n--- config\n    location = /t {\n        rewrite_by_lua_block { print([[Hey, it is \"!]]) } content_by_lua_block { ngx.say([[\"]]) }\n    }\n--- request\nGET /t\n--- response_body\n\"\n--- error_log\nHey, it is \"!\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: single quotes in long brackets\n--- config\n    location = /t {\n        rewrite_by_lua_block { print([[Hey, it is '!]]) } content_by_lua_block { ngx.say([[']]) }\n    }\n--- request\nGET /t\n--- response_body\n'\n--- error_log\nHey, it is '!\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: lexer no match due to incomplete data chunks in a fixed size buffer\n--- config\n        location /test1 {\n            content_by_lua_block {\n                ngx.say(\"1: this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\")\n            }\n        }\n        location /test2 {\n            content_by_lua_block {\n                ngx.say(\"2: this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\")\n            }\n        }\n\n        location /test3 {\n            content_by_lua_block {\n                ngx.say(\"3: this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\",\n                \"this is just some random filler to cause an error\")\n            }\n        }\n--- request\nGET /test3\n--- response_body eval\n\"3: \" . (\"this is just some random filler to cause an error\" x 20) . \"\\n\"\n--- no_error_log\n[error]\n\n\n\n=== TEST 23: lexer should not stop lexing in the middle of a value\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.say(\"}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                }\")\n            ngx.say(\"}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                }\")\n        }\n    }\n--- request\nGET /t\n--- response_body_like: }\n--- no_error_log\n[error]\n\n\n\n=== TEST 24: too long bracket\n--- config\n    location = /t {\n        content_by_lua_block {\n            local foo = [[\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n            ]]\n            ngx.say(foo)\n        }\n    }\n--- request\nGET /t\n--- response_body\nhello, world\n--- no_error_log\n[error]\n--- must_die\n--- error_log eval\nqr/\\[emerg\\] .*? Lua code block missing the closing long bracket \"]]\", the inlined Lua code may be too long in .*?\\bnginx\\.conf:\\d+/\n"
  },
  {
    "path": "t/133-worker-count.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: content_by_lua\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"workers: \", ngx.worker.count())\n        }\n    }\n--- request\nGET /lua\n--- response_body\nworkers: 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: init_by_lua\n--- http_config\n    init_by_lua_block {\n        package.loaded.count = ngx.worker.count()\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"workers: \", package.loaded.count)\n        }\n    }\n--- request\nGET /lua\n--- response_body\nworkers: 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: init_worker_by_lua\n--- http_config\n    init_worker_by_lua_block {\n        init_worker_count = ngx.worker.count()\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"workers: \", init_worker_count)\n        }\n    }\n--- request\nGET /lua\n--- response_body\nworkers: 1\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/134-worker-count-5.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\nmaster_on();\nworkers(5);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"worker count: \", ngx.worker.count())\n        }\n    }\n--- request\nGET /lua\n--- response_body\nworker count: 5\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: init_by_lua\n--- http_config\n    init_by_lua_block {\n        package.loaded.count = ngx.worker.count()\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"workers: \", package.loaded.count)\n        }\n    }\n--- request\nGET /lua\n--- response_body\nworkers: 5\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: init_by_lua + module (github #681)\n--- http_config\n    lua_package_path \"$TEST_NGINX_SERVER_ROOT/html/?.lua;;\";\n\n    init_by_lua_block {\n        local blah = require \"file\"\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"ok\")\n        }\n    }\n--- user_files\n>>> file.lua\nlocal timer_interval = 1\nlocal time_factor = timer_interval / (ngx.worker.count() * 60)\n--- request\nGET /lua\n--- response_body\nok\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/135-worker-id.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\nmaster_on();\nworkers(2);\n#log_level('warn');\n\n#repeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"worker id: \", ngx.worker.id())\n        }\n    }\n--- request\nGET /lua\n--- response_body_like chop\n^worker id: [0-1]$\n--- no_error_log\n[error]\n--- skip_nginx: 3: <=1.9.0\n"
  },
  {
    "path": "t/136-timer-counts.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(1);\n\nplan tests => blocks() * (repeat_each() * 3);\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: running count with no running timers\n--- config\n    location /timers {\n        content_by_lua_block { ngx.say(ngx.timer.running_count()) }\n    }\n--- request\nGET /timers\n--- response_body\n0\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: running count with no pending timers\n--- config\n    location /timers {\n        content_by_lua_block { ngx.say(ngx.timer.pending_count()) }\n    }\n--- request\nGET /timers\n--- response_body\n0\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: pending count with one pending timer\n--- config\n    location /timers {\n        content_by_lua_block {\n            ngx.timer.at(3, function() end)\n            ngx.say(ngx.timer.pending_count())\n        }\n    }\n--- request\nGET /timers\n--- response_body\n1\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: pending count with 3 pending timers\n--- config\n    location /timers {\n        content_by_lua_block {\n            ngx.timer.at(4, function() end)\n            ngx.timer.at(2, function() end)\n            ngx.timer.at(1, function() end)\n            ngx.say(ngx.timer.pending_count())\n        }\n    }\n--- request\nGET /timers\n--- response_body\n3\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: one running timer\n--- config\n    location /timers {\n        content_by_lua_block {\n            ngx.timer.at(0.1, function() ngx.sleep(0.3) end)\n            ngx.sleep(0.2)\n            ngx.say(ngx.timer.running_count())\n        }\n    }\n--- request\nGET /timers\n--- response_body\n1\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: 3 running timers\n--- config\n    location /timers {\n        content_by_lua_block {\n            ngx.timer.at(0.1, function() ngx.sleep(0.3) end)\n            ngx.timer.at(0.11, function() ngx.sleep(0.3) end)\n            ngx.timer.at(0.09, function() ngx.sleep(0.3) end)\n            ngx.sleep(0.2)\n            ngx.say(ngx.timer.running_count())\n        }\n    }\n--- request\nGET /timers\n--- response_body\n3\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/137-req-misc.t",
    "content": "use Test::Nginx::Socket::Lua;\n\n#master_on();\n#workers(1);\n#worker_connections(1014);\n#log_level('warn');\n#master_process_enabled(1);\n\nrepeat_each(2);\n\nplan tests => repeat_each() * blocks() * 2;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\n#no_diff();\nno_long_string();\n#no_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: not internal request\n--- config\n    location /test {\n        rewrite ^/test$ /lua last;\n    }\n    location /lua {\n        content_by_lua '\n            if ngx.req.is_internal() then\n                ngx.say(\"internal\")\n            else\n                ngx.say(\"not internal\")\n            end\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nnot internal\n\n\n\n=== TEST 2: internal request\n--- config\n    location /test {\n        rewrite ^/test$ /lua last;\n    }\n    location /lua {\n        content_by_lua '\n            if ngx.req.is_internal() then\n                ngx.say(\"internal\")\n            else\n                ngx.say(\"not internal\")\n            end\n        ';\n    }\n--- request\nGET /test\n--- response_body\ninternal\n"
  },
  {
    "path": "t/138-balancer-upstream-bind.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: bind to empty\n--- no_http2\n--- http_config\n    lua_package_path \"$TEST_NGINX_SERVER_ROOT/html/?.lua;;\";\n\n    upstream backend {\n        server 127.0.0.1:$TEST_NGINX_SERVER_PORT;\n    }\n--- config\n    set $proxy_local_addr \"\";\n    proxy_bind $proxy_local_addr;\n\n    location = /t {\n        proxy_pass http://backend/back;\n    }\n\n    location = /back {\n        echo ok;\n    }\n\n--- request\n    GET /t\n--- response_body\nok\n--- no_error_log\n[cirt]\n\n\n\n=== TEST 2: bind to 127.0.0.1\n--- no_http2\n--- http_config\n    lua_package_path \"$TEST_NGINX_SERVER_ROOT/html/?.lua;;\";\n\n    upstream backend {\n        server 127.0.0.1:$TEST_NGINX_SERVER_PORT;\n    }\n--- config\n    set $proxy_local_addr \"\";\n    proxy_bind $proxy_local_addr;\n\n    location = /t {\n        access_by_lua_block {\n            ngx.var.proxy_local_addr=\"127.0.0.1\"\n        }\n        proxy_pass http://backend/back;\n    }\n\n    location = /back {\n        echo ok;\n    }\n\n--- request\n    GET /t\n--- response_body\nok\n--- no_error_log\n[cirt]\n\n\n\n=== TEST 3: bind to 127.0.0.10\n--- no_http2\n--- http_config\n    lua_package_path \"$TEST_NGINX_SERVER_ROOT/html/?.lua;;\";\n\n    upstream backend {\n        server 127.0.0.1:$TEST_NGINX_SERVER_PORT;\n    }\n--- config\n    set $proxy_local_addr \"\";\n    proxy_bind $proxy_local_addr;\n\n    location = /t {\n        access_by_lua_block {\n            ngx.var.proxy_local_addr=\"127.0.0.10\"\n        }\n        proxy_pass http://backend/back;\n    }\n\n    location = /back {\n        echo ok;\n    }\n\n--- request\n    GET /t\n--- response_body\nok\n--- no_error_log\n[cirt]\n\n\n\n=== TEST 4: bind to not exist addr 100.100.100.100\n--- no_http2\n--- http_config\n    lua_package_path \"$TEST_NGINX_SERVER_ROOT/html/?.lua;;\";\n\n    upstream backend {\n        server 127.0.0.1:$TEST_NGINX_SERVER_PORT;\n    }\n--- config\n    set $proxy_local_addr \"\";\n    proxy_bind $proxy_local_addr;\n\n    location = /t {\n        access_by_lua_block {\n            ngx.var.proxy_local_addr=\"100.100.100.100\"\n        }\n        proxy_pass http://backend/back;\n    }\n\n    location = /back {\n        echo ok;\n    }\n\n--- request\n    GET /t\n--- response_body_like chomp\n500 Internal Server Error\n--- error_code: 500\n--- error_log\nbind(100.100.100.100) failed (99: Cannot assign requested address)\n"
  },
  {
    "path": "t/138-balancer.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\n#connect 0.0.0.1 on newer kernel won't return EINVAL\n#so add an route with cmd: sudo ip route add prohibit 0.0.0.1/32\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4 - 3);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple logging\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            print(\"hello from balancer by lua!\")\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- request\n    GET /t\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- error_log eval\n[\n'[lua] balancer_by_lua(nginx.conf:27):2: hello from balancer by lua! while connecting to upstream,',\nqr{\\[crit\\] .*? connect\\(\\) to 0\\.0\\.0\\.1:80 failed .*?, upstream: \"http://0\\.0\\.0\\.1:80/t\"},\n]\n\n\n\n=== TEST 2: exit 403\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            print(\"hello from balancer by lua!\")\n            ngx.exit(403)\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- request\n    GET /t\n--- response_body_like: 403 Forbidden\n--- error_code: 403\n--- error_log\n[lua] balancer_by_lua(nginx.conf:27):2: hello from balancer by lua! while connecting to upstream,\n--- no_error_log eval\n[\nqr{\\[crit\\] .*? connect\\(\\) to 0\\.0\\.0\\.1:80 failed .*?, upstream: \"http://0\\.0\\.0\\.1:80/t\"},\n]\n\n\n\n=== TEST 3: exit OK\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            print(\"hello from balancer by lua!\")\n            ngx.exit(ngx.OK)\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- request\n    GET /t\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- error_log eval\n[\n'[lua] balancer_by_lua(nginx.conf:27):2: hello from balancer by lua! while connecting to upstream,',\nqr{\\[crit\\] .*? connect\\(\\) to 0\\.0\\.0\\.1:80 failed .*?, upstream: \"http://0\\.0\\.0\\.1:80/t\"},\n]\n\n\n\n=== TEST 4: ngx.var works\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            print(\"1: variable foo = \", ngx.var.foo)\n            ngx.var.foo = tonumber(ngx.var.foo) + 1\n            print(\"2: variable foo = \", ngx.var.foo)\n        }\n    }\n--- config\n    location = /t {\n        set $foo 32;\n        proxy_pass http://backend;\n    }\n--- request\n    GET /t\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- error_log eval\n[\n\"1: variable foo = 32\",\n\"2: variable foo = 33\",\nqr/\\[crit\\] .* connect\\(\\) .*? failed/,\n]\n\n\n\n=== TEST 5: ngx.req.get_headers works\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            print(\"header foo: \", ngx.req.get_headers()[\"foo\"])\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- request\n    GET /t\n--- more_headers\nFoo: bar\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- error_log eval\n[\n\"header foo: bar\",\nqr/\\[crit\\] .* connect\\(\\) .*? failed/,\n]\n\n\n\n=== TEST 6: ngx.req.get_uri_args() works\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            print(\"arg foo: \", (ngx.req.get_uri_args())[\"foo\"])\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- request\n    GET /t?baz=blah&foo=bar\n--- more_headers\nFoo: bar\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- error_log eval\n[\"arg foo: bar\",\nqr/\\[crit\\] .* connect\\(\\) .*? failed/,\n]\n\n\n\n=== TEST 7: ngx.req.get_method() works\n--- no_http2\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            print(\"method: \", ngx.req.get_method())\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- request\n    GET /t\n--- more_headers\nFoo: bar\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- error_log eval\n[\n\"method: GET\",\nqr/\\[crit\\] .* connect\\(\\) .*? failed/,\n]\n\n\n\n=== TEST 8: simple logging (by_lua_file)\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_file html/a.lua;\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- user_files\n>>> a.lua\nprint(\"hello from balancer by lua!\")\n--- request\n    GET /t\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- error_log eval\n[\n'[lua] a.lua:1: hello from balancer by lua! while connecting to upstream,',\nqr{\\[crit\\] .*? connect\\(\\) to 0\\.0\\.0\\.1:80 failed .*?, upstream: \"http://0\\.0\\.0\\.1:80/t\"},\n]\n\n\n\n=== TEST 9: cosockets are disabled\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            local sock, err = ngx.socket.tcp()\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- request\n    GET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/\\[error\\] .*? failed to run balancer_by_lua\\*: balancer_by_lua\\(nginx\\.conf:27\\):2: API disabled in the context of balancer_by_lua\\*/\n\n\n\n=== TEST 10: ngx.sleep is disabled\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            ngx.sleep(0.1)\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- request\n    GET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\nqr/\\[error\\] .*? failed to run balancer_by_lua\\*: balancer_by_lua\\(nginx\\.conf:27\\):2: API disabled in the context of balancer_by_lua\\*/\n\n\n\n=== TEST 11: get_phase\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            print(\"I am in phase \", ngx.get_phase())\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- request\n    GET /t\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- grep_error_log eval: qr/I am in phase \\w+/\n--- grep_error_log_out\nI am in phase balancer\n--- error_log eval\nqr{\\[crit\\] .*? connect\\(\\) to 0\\.0\\.0\\.1:80 failed .*?, upstream: \"http://0\\.0\\.0\\.1:80/t\"}\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: code cache off\n--- no_http2\n--- http_config\n    lua_package_path \"$TEST_NGINX_SERVER_ROOT/html/?.lua;;\";\n\n    lua_code_cache off;\n\n    upstream backend {\n        server 127.0.0.1:$TEST_NGINX_SERVER_PORT;\n        balancer_by_lua_block {\n            require(\"test\")\n        }\n    }\n--- config\n    location = /t {\n        echo_location /main;\n        echo_location /update;\n        echo_location /main;\n    }\n\n    location = /update {\n        content_by_lua_block {\n            -- os.execute(\"(echo HERE; pwd) > /dev/stderr\")\n            local f = assert(io.open(\"$TEST_NGINX_SERVER_ROOT/html/test.lua\", \"w\"))\n            f:write(\"print('me: ', 101)\")\n            f:close()\n            ngx.say(\"updated\")\n        }\n    }\n\n    location = /main {\n        proxy_pass http://backend/back;\n    }\n\n    location = /back {\n        echo ok;\n    }\n--- request\n    GET /t\n--- user_files\n>>> test.lua\nprint(\"me: \", 32)\nreturn {}\n--- response_body\nok\nupdated\nok\n--- grep_error_log eval: qr/\\bme: \\w+/\n--- grep_error_log_out\nme: 32\nme: 101\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: lua subrequests\n--- http_config\n    lua_code_cache off;\n\n    upstream backend {\n        server 127.0.0.1:$TEST_NGINX_SERVER_PORT;\n        balancer_by_lua_block {\n            print(\"ctx counter: \", ngx.ctx.count)\n            if not ngx.ctx.count then\n                ngx.ctx.count = 1\n            else\n                ngx.ctx.count = ngx.ctx.count + 1\n            end\n        }\n    }\n--- config\n    location = /t {\n        content_by_lua_block {\n            local res = ngx.location.capture(\"/main\")\n            ngx.print(res.body)\n            res = ngx.location.capture(\"/main\")\n            ngx.print(res.body)\n        }\n    }\n\n    location = /main {\n        proxy_pass http://backend/back;\n    }\n\n    location = /back {\n        echo ok;\n    }\n--- request\n    GET /t\n--- response_body\nok\nok\n--- grep_error_log eval: qr/\\bctx counter: \\w+/\n--- grep_error_log_out\nctx counter: nil\nctx counter: nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: ngx.log(ngx.ERR, ...) github #816\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            ngx.log(ngx.ERR, \"hello from balancer by lua!\")\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- request\n    GET /t\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- error_log eval\n[\n'[lua] balancer_by_lua(nginx.conf:27):2: hello from balancer by lua! while connecting to upstream,',\nqr{\\[crit\\] .*? connect\\(\\) to 0\\.0\\.0\\.1:80 failed .*?, upstream: \"http://0\\.0\\.0\\.1:80/t\"},\n]\n\n\n\n=== TEST 15: test if exceed proxy_next_upstream_limit\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;;\";\n\n    proxy_next_upstream_tries 5;\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            local b = require \"ngx.balancer\"\n\n            if not ngx.ctx.tries then\n                ngx.ctx.tries = 0\n            end\n\n            if ngx.ctx.tries >= 6 then\n                ngx.log(ngx.ERR, \"retry count exceed limit\")\n                ngx.exit(500)\n            end\n\n            ngx.ctx.tries = ngx.ctx.tries + 1\n            print(\"retry counter: \", ngx.ctx.tries)\n\n            local ok, err = b.set_more_tries(2)\n            if not ok then\n                return error(\"failed to set more tries: \", err)\n            elseif err then\n                ngx.log(ngx.WARN, \"set more tries: \", err)\n            end\n\n            assert(b.set_current_peer(\"127.0.0.1\", 81))\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend/back;\n    }\n\n    location = /back {\n        return 404;\n    }\n--- request\n    GET /t\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- grep_error_log eval: qr/\\bretry counter: \\w+/\n--- grep_error_log_out\nretry counter: 1\nretry counter: 2\nretry counter: 3\nretry counter: 4\nretry counter: 5\n\n--- error_log\nset more tries: reduced tries due to limit\n\n\n\n=== TEST 16: set_more_tries bugfix\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;;\";\n\tproxy_next_upstream_tries 0;\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            local balancer = require \"ngx.balancer\"\n\t\t\tlocal ctx = ngx.ctx\n\t\t\tif not ctx.has_run then\n\t\t\t\tctx.has_run = true\n\t\t\t\tlocal _, err = balancer.set_more_tries(3)\n\t\t\t\tif err then\n\t\t\t\t\tngx.log(ngx.ERR, \"failed to set more tries: \", err)\n\t\t\t\tend\n\t\t\tend\n\t\t\tbalancer.set_current_peer(\"127.0.0.1\", 81)\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- request\n    GET /t\n--- error_code: 502\n--- grep_error_log eval: qr/http next upstream, \\d+/\n--- grep_error_log_out\nhttp next upstream, 2\nhttp next upstream, 2\nhttp next upstream, 2\nhttp next upstream, 2\n--- no_error_log\nfailed to set more tries: reduced tries due to limit\n[alert]\n\n\n\n=== TEST 17: recreate_request buffer bugfix\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;;\";\n\n    server {\n        listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1;\n\n        location / {\n            return 200 \"it works\";\n        }\n    }\n\n    upstream foo {\n        server 127.0.0.1:$TEST_NGINX_RAND_PORT_1 max_fails=0;\n        server 127.0.0.1:$TEST_NGINX_RAND_PORT_2 max_fails=0 weight=9999;\n\n        balancer_by_lua_block {\n            local bal = require \"ngx.balancer\"\n\n            assert(bal.recreate_request())\n        }\n    }\n\n--- config\n    location = /t {\n        proxy_http_version 1.1;\n        proxy_set_header Connection \"\";\n        proxy_pass http://foo;\n    }\n--- request\nGET /t\n--- error_code: 200\n--- error_log\nconnect() failed (111: Connection refused) while connecting to upstream\n--- no_error_log\nupstream sent more data than specified in \"Content-Length\" header while reading upstream\n[alert]\n\n\n\n=== TEST 18: error in balancer_by_lua_block\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            ngx.say(\"hello\"\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- request\n    GET /t\n--- response_body_like: 500 Internal Server Error\n--- error_code: 500\n--- error_log eval\n \"failed to load inlined Lua code: balancer_by_lua(nginx.conf:27):3: ')' expected (to close '(' at line 2) near '<eof>'\",\n\n\n\n=== TEST 19: disable ssl\n--- http_config\n    lua_package_path \"$TEST_NGINX_SERVER_ROOT/html/?.lua;;\";\n\n    upstream backend {\n        server 127.0.0.1:$TEST_NGINX_SERVER_PORT;\n        balancer_by_lua_block {\n            local ffi = require \"ffi\"\n            local C = ffi.C\nffi.cdef[[\nint\nngx_http_lua_ffi_balancer_set_upstream_tls(ngx_http_request_t *r, int on, char **err);\n]]\n            local errmsg = ffi.new(\"char *[1]\")\n            local r = require \"resty.core.base\" .get_request()\n            if r == nil then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            local rc = C.ngx_http_lua_ffi_balancer_set_upstream_tls(r, 0, errmsg)\n            if rc < 0 then\n                ngx.log(ngx.ERR, \"failed to disable ssl: \", ffi.string(errmsg[0]))\n                return\n            end\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass https://backend/back;\n    }\n\n    location = /back {\n        echo ok;\n    }\n\n--- request\n    GET /t\n--- response_body\nok\n--- no_error_log\n[error]\n[cirt]\n\n\n\n=== TEST 20: recreate_request refresh body buffer when ngx.req.set_body_data is used in balancer phase\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;;\";\n\n    server {\n        listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1;\n\n        location / {\n            content_by_lua_block {\n                ngx.req.read_body()\n                local body = ngx.req.get_body_data()\n                ngx.log(ngx.ERR, \"body: \", body)\n                ngx.say(body)\n            }\n        }\n    }\n\n    upstream foo {\n        server 127.0.0.1:$TEST_NGINX_RAND_PORT_1 max_fails=0;\n\n        balancer_by_lua_block {\n            local bal = require \"ngx.balancer\"\n            ngx.req.set_body_data(\"hello world\")\n            assert(bal.recreate_request())\n        }\n    }\n\n--- config\n    location = /t {\n        proxy_http_version 1.1;\n        proxy_set_header Connection \"\";\n        proxy_pass http://foo;\n    }\n--- request\nGET /t\n--- error_code: 200\n--- response_body\nhello world\n\n\n\n=== TEST 21: http2 upstream\n--- http_config\n    lua_package_path        \"../lua-resty-core/lib/?.lua;;\";\n    ssl_certificate     ../../cert/test.crt;\n    ssl_certificate_key ../../cert/test.key;\n    proxy_ssl_verify        off;\n\n    server {\n        listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1 ssl;\n        http2 on;\n\n        location / {\n            content_by_lua_block {\n                ngx.say(\"hello world: \", ngx.var.server_protocol)\n            }\n        }\n    }\n\n    upstream foo {\n        server 127.0.0.1:$TEST_NGINX_RAND_PORT_1 max_fails=0;\n    }\n--- config\n    location = /t {\n        proxy_http_version 2;\n        proxy_set_header Connection \"\";\n        proxy_pass https://foo;\n    }\n--- request\nGET /t\n--- error_code: 200\n--- response_body\nhello world: HTTP/2.0\n--- no_error_log\n[error]\n[crit]\n--- skip_nginx: 4: < 1.29.4\n\n\n\n=== TEST 22: upstream does not support http2\n--- http_config\n    lua_package_path    \"../lua-resty-core/lib/?.lua;;\";\n    ssl_certificate     ../../cert/test.crt;\n    ssl_certificate_key ../../cert/test.key;\n    proxy_ssl_verify    off;\n\n    server {\n        listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1 ssl;\n\n        location / {\n            content_by_lua_block {\n                ngx.say(\"hello world: \", ngx.var.server_protocol)\n            }\n        }\n    }\n\n    upstream foo {\n        server 127.0.0.1:$TEST_NGINX_RAND_PORT_1 max_fails=0;\n    }\n--- config\n    location = /t {\n        proxy_http_version 2;\n        proxy_set_header Connection \"\";\n        proxy_pass https://foo;\n    }\n--- request\nGET /t\n--- error_code: 502\n--- response_body eval\nqr/502 Bad Gateway/\n--- no_error_log\n[error]\n--- error_log eval\nqr/SSL_do_handshake.*(?:no application protocol|NO_APPLICATION_PROTOCOL).*SSL alert number 120\\) while SSL handshaking to upstream/\n--- skip_nginx: 4: < 1.29.4\n"
  },
  {
    "path": "t/139-ssl-cert-by.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(3);\n\n# All these tests need to have new openssl\nmy $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';\nmy $openssl_version = eval { `$NginxBinary -V 2>&1` };\nif ($openssl_version =~ m/BoringSSL/) {\n    $ENV{TEST_NGINX_USE_BORINGSSL} = 1;\n}\n\nif ($openssl_version =~ m/built with OpenSSL (0|1\\.0\\.(?:0|1[^\\d]|2[a-d]).*)/) {\n    plan(skip_all => \"too old OpenSSL, need >= 1.0.2e, was $1\");\n} else {\n    plan tests => repeat_each() * (blocks() * 6 + 4);\n}\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_QUIC_IDLE_TIMEOUT} ||= 0.6;\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple logging\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate_by_lua_block { print(\"ssl cert by lua is running!\") }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\n\n--- no_error_log\n[error]\n[alert]\n--- grep_error_log eval: qr/ssl_certificate_by_lua\\(nginx.conf:\\d+\\):.*?,|\\bssl cert: connection reusable: \\d+|\\breusable connection: \\d+/\n--- grep_error_log_out eval\n# Since nginx version 1.17.9, nginx call ngx_reusable_connection(c, 0)\n# before call ssl callback function\n$Test::Nginx::Util::NginxVersion >= 1.017009 ?\nqr/reusable connection: 0\nssl cert: connection reusable: 0\nssl_certificate_by_lua\\(nginx.conf:28\\):1: ssl cert by lua is running!,/\n: qr /reusable connection: 1\nssl cert: connection reusable: 1\nreusable connection: 0\nssl_certificate_by_lua\\(nginx.conf:28\\):1: ssl cert by lua is running!,/\n\n\n\n=== TEST 2: sleep\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate_by_lua_block {\n            local begin = ngx.now()\n            ngx.sleep(0.1)\n            print(\"elapsed in ssl cert by lua: \", ngx.now() - begin)\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log eval\n[\n'lua ssl server name: \"test.com\"',\nqr/elapsed in ssl cert by lua: 0.(?:09|1\\d)\\d+,/,\n]\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 3: timer\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate_by_lua_block {\n            local function f()\n                print(\"my timer run!\")\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to create timer: \", err)\n                return\n            end\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nmy timer run!\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 4: cosocket\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            sock:settimeout(2000)\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to connect to memc: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"flush_all\\r\\n\")\n            if not bytes then\n                ngx.log(ngx.ERR, \"failed to send flush_all command: \", err)\n                return\n            end\n\n            local res, err = sock:receive()\n            if not res then\n                ngx.log(ngx.ERR, \"failed to receive memc reply: \", err)\n                return\n            end\n\n            print(\"received memc reply: \", res)\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nreceived memc reply: OK\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 5: ngx.exit(0) - no yield\n--- http_config\n    server {\n        listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl;\n        server_name test.com;\n        ssl_certificate_by_lua_block {\n            ngx.exit(0)\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"127.0.0.2\", $TEST_NGINX_RAND_PORT_1)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: boolean\n\n--- error_log\nlua exit with code 0\n\n--- no_error_log\nshould never reached here\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 6: ngx.exit(ngx.ERROR) - no yield\n--- http_config\n    server {\n        listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl;\n        server_name test.com;\n        ssl_certificate_by_lua_block {\n            ngx.exit(ngx.ERROR)\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"127.0.0.2\", $TEST_NGINX_RAND_PORT_1)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- error_log eval\n[\n'ssl_certificate_by_lua: handler return value: -1, cert cb exit code: 0',\nqr/(\\[info\\] .*? SSL_do_handshake\\(\\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/,\n'lua exit with code -1',\n]\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 7: ngx.exit(0) -  yield\n--- http_config\n    server {\n        listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl;\n        server_name test.com;\n        ssl_certificate_by_lua_block {\n            ngx.sleep(0.001)\n            ngx.exit(0)\n\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"127.0.0.2\", $TEST_NGINX_RAND_PORT_1)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: boolean\n\n--- error_log\nlua exit with code 0\n\n--- no_error_log\nshould never reached here\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 8: ngx.exit(ngx.ERROR) - yield\n--- http_config\n    server {\n        listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl;\n        server_name test.com;\n        ssl_certificate_by_lua_block {\n            ngx.sleep(0.001)\n            ngx.exit(ngx.ERROR)\n\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"127.0.0.2\", $TEST_NGINX_RAND_PORT_1)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- error_log eval\n[\n'ssl_certificate_by_lua: cert cb exit code: 0',\nqr/(\\[info\\] .*? SSL_do_handshake\\(\\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/,\n'lua exit with code -1',\n]\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 9: lua exception - no yield\n--- http_config\n    server {\n        listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl;\n        server_name test.com;\n        ssl_certificate_by_lua_block {\n            error(\"bad bad bad\")\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"127.0.0.2\", $TEST_NGINX_RAND_PORT_1)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- error_log eval\n[\n'runtime error: ssl_certificate_by_lua(nginx.conf:28):2: bad bad bad',\n'ssl_certificate_by_lua: handler return value: 500, cert cb exit code: 0',\nqr/(\\[info\\] .*? SSL_do_handshake\\(\\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/,\nqr/context: ssl_certificate_by_lua\\*, client: \\d+\\.\\d+\\.\\d+\\.\\d+, server: \\d+\\.\\d+\\.\\d+\\.\\d+:\\d+/,\n]\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 10: lua exception - yield\n--- http_config\n    server {\n        listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl;\n        server_name test.com;\n        ssl_certificate_by_lua_block {\n            ngx.sleep(0.001)\n            error(\"bad bad bad\")\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"127.0.0.2\", $TEST_NGINX_RAND_PORT_1)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- error_log eval\n[\n'runtime error: ssl_certificate_by_lua(nginx.conf:28):3: bad bad bad',\n'ssl_certificate_by_lua: cert cb exit code: 0',\nqr/(\\[info\\] .*? SSL_do_handshake\\(\\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/,\n]\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 11: get phase\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate_by_lua_block {print(\"get_phase: \", ngx.get_phase())}\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end\n            collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\n\n--- error_log\nlua ssl server name: \"test.com\"\nget_phase: ssl_cert\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 12: connection aborted prematurely\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate_by_lua_block {\n            ngx.sleep(0.3)\n            -- local ssl = require \"ngx.ssl\"\n            -- ssl.clear_certs()\n            print(\"ssl-cert-by-lua: after sleeping\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(150)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n\n--- response_body\nconnected: 1\nfailed to do SSL handshake: timeout\n\n--- error_log\nlua ssl server name: \"test.com\"\nssl-cert-by-lua: after sleeping\n\n--- no_error_log\n[error]\n[alert]\n--- wait: 0.6\n\n\n\n=== TEST 13: subrequests disabled\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate_by_lua_block {ngx.location.capture(\"/foo\")}\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- error_log eval\n[\n'lua ssl server name: \"test.com\"',\n'ssl_certificate_by_lua(nginx.conf:28):1: API disabled in the context of ssl_certificate_by_lua*',\nqr/(\\[info\\] .*? SSL_do_handshake\\(\\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/,\n]\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 14: simple logging (by_lua_file)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate_by_lua_file html/a.lua;\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n\n--- user_files\n>>> a.lua\nprint(\"ssl cert by lua is running!\")\n\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\na.lua:1: ssl cert by lua is running!\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 15: coroutine API\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate_by_lua_block {\n            local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n\n            local function f()\n                local cnt = 0\n                for i = 1, 20 do\n                    print(\"co yield: \", cnt)\n                    cy()\n                    cnt = cnt + 1\n                end\n            end\n\n            local c = cc(f)\n            for i = 1, 3 do\n                print(\"co resume, status: \", coroutine.status(c))\n                cr(c)\n            end\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- grep_error_log eval: qr/co (?:yield: \\d+|resume, status: \\w+)/\n--- grep_error_log_out\nco resume, status: suspended\nco yield: 0\nco resume, status: suspended\nco yield: 1\nco resume, status: suspended\nco yield: 2\n\n--- error_log\nlua ssl server name: \"test.com\"\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 16: simple user thread wait with yielding\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate_by_lua_block {\n            local function f()\n                ngx.sleep(0.01)\n                print(\"uthread: hello in thread\")\n                return \"done\"\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.log(ngx.ERR, \"uthread: failed to spawn thread: \", err)\n                return ngx.exit(ngx.ERROR)\n            end\n\n            print(\"uthread: thread created: \", coroutine.status(t))\n\n            local ok, res = ngx.thread.wait(t)\n            if not ok then\n                print(\"uthread: failed to wait thread: \", res)\n                return\n            end\n\n            print(\"uthread: \", res)\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- no_error_log\n[error]\n[alert]\n--- grep_error_log eval: qr/uthread: [^.,]+/\n--- grep_error_log_out\nuthread: thread created: running\nuthread: hello in thread\nuthread: done\n\n\n\n=== TEST 17: simple logging - use ssl_certificate_by_lua* on the http {} level\nGitHub openresty/lua-resty-core#42\n--- http_config\n    ssl_certificate_by_lua_block { print(\"ssl cert by lua is running!\") }\n    ssl_certificate ../../cert/test.crt;\n    ssl_certificate_key ../../cert/test.key;\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nssl_certificate_by_lua(nginx.conf:25):1: ssl cert by lua is running!\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 18: simple logging (syslog)\ngithub issue #723\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        error_log syslog:server=127.0.0.1:12345 debug;\n\n        ssl_certificate_by_lua_block { print(\"ssl cert by lua is running!\") }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log eval\n[\nqr/\\[error\\] .*? send\\(\\) failed/,\n'lua ssl server name: \"test.com\"',\n]\n--- no_error_log\n[alert]\nssl_certificate_by_lua:1: ssl cert by lua is running!\n\n\n\n=== TEST 19: check the count of running timers\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block { print(\"ssl cert by lua is running!\") }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /timers {\n            default_type 'text/plain';\n            content_by_lua_block {\n                ngx.timer.at(0.1, function() ngx.sleep(0.3) end)\n                ngx.timer.at(0.11, function() ngx.sleep(0.3) end)\n                ngx.timer.at(0.09, function() ngx.sleep(0.3) end)\n                ngx.sleep(0.2)\n                ngx.say(ngx.timer.running_count())\n            }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /timers HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 59 bytes.\nreceived: HTTP/1.1 200 OK\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 2\nreceived: Connection: close\nreceived: \nreceived: 3\nclose: 1 nil\n\n--- error_log eval\n[\n'ssl_certificate_by_lua(nginx.conf:29):1: ssl cert by lua is running!',\n'lua ssl server name: \"test.com\"',\n]\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 20: some server {} block missing ssl_certificate_by_lua* handlers (literal server name)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block { print(\"ssl cert by lua is running!\") }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /timers {\n            default_type 'text/plain';\n            content_by_lua_block {\n                ngx.timer.at(0.1, function() ngx.sleep(0.3) end)\n                ngx.timer.at(0.11, function() ngx.sleep(0.3) end)\n                ngx.timer.at(0.09, function() ngx.sleep(0.3) end)\n                ngx.sleep(0.2)\n                ngx.say(ngx.timer.running_count())\n            }\n            more_clear_headers Date;\n        }\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test2.com;\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test2.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /timers HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- error_log eval\n[\nqr/\\[alert\\] .*? no ssl_certificate_by_lua\\* defined in server test2\\.com\\b/,\nqr/\\[info\\] .*? SSL_do_handshake\\(\\) failed\\b/,\n]\n\n\n\n=== TEST 21: some server {} block missing ssl_certificate_by_lua* handlers (regex server name)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block { print(\"ssl cert by lua is running!\") }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /timers {\n            default_type 'text/plain';\n            content_by_lua_block {\n                ngx.timer.at(0.1, function() ngx.sleep(0.3) end)\n                ngx.timer.at(0.11, function() ngx.sleep(0.3) end)\n                ngx.timer.at(0.09, function() ngx.sleep(0.3) end)\n                ngx.sleep(0.2)\n                ngx.say(ngx.timer.running_count())\n            }\n            more_clear_headers Date;\n        }\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name ~test2\\.com;\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test2.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /timers HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- error_log eval\n[\nqr/\\[alert\\] .*? no ssl_certificate_by_lua\\* defined in server ~test2\\\\\\.com\\b/,\nqr/\\[info\\] .*? SSL_do_handshake\\(\\) failed\\b/,\n]\n\n\n\n=== TEST 22: get raw_client_addr - IPv4\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;;\";\n\n    server {\n        listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1 ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            local ssl = require \"ngx.ssl\"\n            local byte = string.byte\n            local addr, addrtype, err = ssl.raw_client_addr()\n            local ip = string.format(\"%d.%d.%d.%d\", byte(addr, 1), byte(addr, 2),\n                       byte(addr, 3), byte(addr, 4))\n            print(\"client ip: \", ip)\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_RAND_PORT_1)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nclient ip: 127.0.0.1\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 23: get raw_client_addr - unix domain socket\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;;\";\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            local ssl = require \"ngx.ssl\"\n            local addr, addrtyp, err = ssl.raw_client_addr()\n            print(\"client socket file: \", addr)\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nclient socket file:\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 24: ssl_certificate_by_lua* can yield when reading early data\n--- skip_openssl: 6: < 1.1.1\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        ssl_early_data on;\n        server_tokens off;\n\n        ssl_certificate_by_lua_block {\n            local begin = ngx.now()\n            ngx.sleep(0.1)\n            print(\"elapsed in ssl_certificate_by_lua*: \", ngx.now() - begin)\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: boolean\n--- grep_error_log eval\nqr/elapsed in ssl_certificate_by_lua\\*: 0\\.(?:09|1\\d)\\d+,/,\n--- grep_error_log_out eval\n[\nqr/elapsed in ssl_certificate_by_lua\\*: 0\\.(?:09|1\\d)\\d+,/,\nqr/elapsed in ssl_certificate_by_lua\\*: 0\\.(?:09|1\\d)\\d+,/,\nqr/elapsed in ssl_certificate_by_lua\\*: 0\\.(?:09|1\\d)\\d+,/,\n]\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 25: cosocket (UDP)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        server_tokens off;\n\n        ssl_certificate_by_lua_block {\n            local sock = ngx.socket.udp()\n\n            sock:settimeout(1000)\n\n            local ok, err = sock:setpeername(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to connect to memc: \", err)\n                return\n            end\n\n            local req = \"\\0\\1\\0\\0\\0\\1\\0\\0flush_all\\r\\n\"\n            local ok, err = sock:send(req)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to send flush_all to memc: \", err)\n                return\n            end\n\n            local res, err = sock:receive()\n            if not res then\n                ngx.log(ngx.ERR, \"failed to receive memc reply: \", err)\n                return\n            end\n\n            ngx.log(ngx.INFO, \"received memc reply of \", #res, \" bytes\")\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\n--- no_error_log\n[error]\n[alert]\n[emerg]\n--- grep_error_log eval: qr/received memc reply of \\d+ bytes/\n--- grep_error_log_out eval\n[\n'received memc reply of 12 bytes\n',\n'received memc reply of 12 bytes\n',\n'received memc reply of 12 bytes\n',\n'received memc reply of 12 bytes\n',\n]\n\n\n\n=== TEST 26: uthread (kill)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        server_tokens off;\n\n        ssl_certificate_by_lua_block {\n            local function f()\n                ngx.log(ngx.INFO, \"uthread: hello from f()\")\n                ngx.sleep(1)\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.log(ngx.ERR, \"failed to spawn thread: \", err)\n                return ngx.exit(ngx.ERROR)\n            end\n\n            local ok, res = ngx.thread.kill(t)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to kill thread: \", res)\n                return\n            end\n\n            ngx.log(ngx.INFO, \"uthread: killed\")\n\n            local ok, err = ngx.thread.kill(t)\n            if not ok then\n                ngx.log(ngx.INFO, \"uthread: failed to kill: \", err)\n            end\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\n--- no_error_log\n[error]\n[alert]\n[emerg]\n--- grep_error_log eval: qr/uthread: [^.,]+/\n--- grep_error_log_out\nuthread: hello from f()\nuthread: killed\nuthread: failed to kill: already waited or killed\n\n\n\n=== TEST 27: ssl_certificate_by_lua* with TCP cosocket (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_certificate_by_lua_block {\n        print(\"ssl certificate: TCP cosocket test start\")\n\n        local sock = ngx.socket.tcp()\n        sock:settimeout(2000)\n\n        local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to connect to memc: \", err)\n            return\n        end\n\n        local bytes, err = sock:send(\"flush_all\\r\\n\")\n        if not bytes then\n            ngx.log(ngx.ERR, \"failed to send flush_all command: \", err)\n            return\n        end\n\n        local res, err = sock:receive()\n        if not res then\n            ngx.log(ngx.ERR, \"failed to receive memc reply: \", err)\n            return\n        end\n\n        print(\"ssl certificate: received TCP memc reply: \", res)\n        sock:close()\n        print(\"ssl certificate: TCP cosocket test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out\nssl certificate: TCP cosocket test start\nssl certificate: received TCP memc reply: OK\nssl certificate: TCP cosocket test done\ntest completed\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 28: ssl_certificate_by_lua* with UDP cosocket (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_certificate_by_lua_block {\n        print(\"ssl certificate: UDP cosocket test start\")\n\n        local sock = ngx.socket.udp()\n        sock:settimeout(1000)\n\n        local ok, err = sock:setpeername(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to connect to memc: \", err)\n            return\n        end\n\n        local req = \"\\0\\1\\0\\0\\0\\1\\0\\0flush_all\\r\\n\"\n        local ok, err = sock:send(req)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to send flush_all to memc: \", err)\n            return\n        end\n\n        local res, err = sock:receive()\n        if not res then\n            ngx.log(ngx.ERR, \"failed to receive memc reply: \", err)\n            return\n        end\n\n        print(\"ssl certificate: received UDP memc reply of \", #res, \" bytes\")\n        sock:close()\n        print(\"ssl certificate: UDP cosocket test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out eval\n[\nqr/ssl certificate: UDP cosocket test start/,\nqr/ssl certificate: received UDP memc reply of 12 bytes/,\nqr/ssl certificate: UDP cosocket test done/,\nqr/test completed/,\n]\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 29: ssl_certificate_by_lua* with ngx.timer (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_certificate_by_lua_block {\n        print(\"ssl certificate: timer test start\")\n\n        local function timer_handler()\n            print(\"ssl certificate: timer executed\")\n        end\n\n        local ok, err = ngx.timer.at(0, timer_handler)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to create timer: \", err)\n            return\n        end\n\n        print(\"ssl certificate: timer created\")\n        print(\"ssl certificate: timer test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out\nssl certificate: timer test start\nssl certificate: timer created\nssl certificate: timer test done\nssl certificate: timer executed\ntest completed\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 30: ssl_certificate_by_lua* with user threads (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_certificate_by_lua_block {\n        print(\"ssl certificate: uthread test start\")\n\n        local function worker()\n            ngx.sleep(0.01)\n            print(\"ssl certificate: uthread worker executed\")\n            return \"worker_result\"\n        end\n\n        local t, err = ngx.thread.spawn(worker)\n        if not t then\n            ngx.log(ngx.ERR, \"failed to spawn thread: \", err)\n            return\n        end\n\n        print(\"ssl certificate: uthread spawned\")\n\n        local ok, res = ngx.thread.wait(t)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to wait thread: \", res)\n            return\n        end\n\n        print(\"ssl certificate: uthread result: \", res)\n        print(\"ssl certificate: uthread test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out\nssl certificate: uthread test start\nssl certificate: uthread spawned\nssl certificate: uthread worker executed\nssl certificate: uthread result: worker_result\nssl certificate: uthread test done\ntest completed\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 31: ssl_certificate_by_lua* with coroutines (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_certificate_by_lua_block {\n        print(\"ssl certificate: coroutine test start\")\n\n        local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n\n        local function coro_func()\n            local cnt = 0\n            for i = 1, 3 do\n                print(\"ssl certificate: coro yield: \", cnt)\n                cy()\n                cnt = cnt + 1\n            end\n            return \"coro_done\"\n        end\n\n        local c = cc(coro_func)\n        for i = 1, 4 do\n            print(\"ssl certificate: coro resume, status: \", coroutine.status(c))\n            local ok, res = cr(c)\n            if not ok then\n                print(\"ssl certificate: coro error: \", res)\n                break\n            end\n            if coroutine.status(c) == \"dead\" then\n                print(\"ssl certificate: coro result: \", res)\n                break\n            end\n        end\n\n        print(\"ssl certificate: coroutine test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out\nssl certificate: coroutine test start\nssl certificate: coro resume, status: suspended\nssl certificate: coro yield: 0\nssl certificate: coro resume, status: suspended\nssl certificate: coro yield: 1\nssl certificate: coro resume, status: suspended\nssl certificate: coro yield: 2\nssl certificate: coro resume, status: suspended\nssl certificate: coro result: coro_done\nssl certificate: coroutine test done\ntest completed\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 32: ssl_certificate_by_lua* without yield API (simple logic)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_certificate_by_lua_block {\n        print(\"ssl certificate: simple test start\")\n\n        -- Simple calculations without yield\n        local sum = 0\n        for i = 1, 10 do\n            sum = sum + i\n        end\n\n        print(\"ssl certificate: calculated sum: \", sum)\n\n        -- String operations\n        local str = \"hello\"\n        str = str .. \" world\"\n        print(\"ssl certificate: concatenated string: \", str)\n\n        -- Table operations\n        local t = {a = 1, b = 2, c = 3}\n        local count = 0\n        for k, v in pairs(t) do\n            count = count + v\n        end\n        print(\"ssl certificate: table sum: \", count)\n\n        print(\"ssl certificate: simple test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out\nssl certificate: simple test start\nssl certificate: calculated sum: 55\nssl certificate: concatenated string: hello world\nssl certificate: table sum: 6\nssl certificate: simple test done\ntest completed\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 33: ssl_certificate_by_lua* with multiple network operations (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_certificate_by_lua_block {\n        print(\"ssl certificate: multiple network test start\")\n\n        -- First TCP operation\n        local sock1 = ngx.socket.tcp()\n        sock1:settimeout(2000)\n        local ok, err = sock1:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if ok then\n            local bytes, err = sock1:send(\"version\\r\\n\")\n            if bytes then\n                local res, err = sock1:receive()\n                if res then\n                    print(\"ssl certificate: TCP1 version: \", res)\n                end\n            end\n            sock1:close()\n        end\n\n        ngx.sleep(0.01)  -- Small delay\n\n        -- Second UDP operation\n        local sock2 = ngx.socket.udp()\n        sock2:settimeout(1000)\n        local ok, err = sock2:setpeername(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if ok then\n            local req = \"\\0\\1\\0\\0\\0\\1\\0\\0version\\r\\n\"\n            local ok, err = sock2:send(req)\n            if ok then\n                local res, err = sock2:receive()\n                if res then\n                    print(\"ssl certificate: UDP version reply length: \", #res)\n                end\n            end\n            sock2:close()\n        end\n\n        print(\"ssl certificate: multiple network test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out eval\n[\nqr/ssl certificate: multiple network test start/,\nqr/ssl certificate: TCP1 version: VERSION/,\nqr/ssl certificate: UDP version reply length: \\d+/,\nqr/ssl certificate: multiple network test done/,\nqr/test completed/,\n]\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n"
  },
  {
    "path": "t/140-ssl-c-api.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(3);\n\n# All these tests need to have new openssl\nmy $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';\nmy $openssl_version = eval { `$NginxBinary -V 2>&1` };\n\nif ($openssl_version =~ m/built with OpenSSL (0|1\\.0\\.(?:0|1[^\\d]|2[a-d]).*)/) {\n    plan(skip_all => \"too old OpenSSL, need >= 1.0.2e, was $1\");\n} elsif ($openssl_version =~ m/BoringSSL/) {\n    $ENV{TEST_NGINX_USE_BORINGSSL} = 1;\n    plan tests => repeat_each() * (blocks() * 6 - 6);\n} else {\n    plan tests => repeat_each() * (blocks() * 5 - 5);\n    $ENV{TEST_NGINX_USE_OPENSSL} = 1;\n}\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\n\nadd_block_preprocessor(sub {\n    my $block = shift;\n\n    if (!defined $block->user_files) {\n        $block->set_value(\"user_files\", <<'_EOC_');\n>>> defines.lua\nlocal ffi = require \"ffi\"\n\nffi.cdef[[\n    int ngx_http_lua_ffi_cert_pem_to_der(const unsigned char *pem,\n        size_t pem_len, unsigned char *der, char **err);\n\n    int ngx_http_lua_ffi_priv_key_pem_to_der(const unsigned char *pem,\n        size_t pem_len, const unsigned char *passphrase,\n        unsigned char *der, char **err);\n\n    int ngx_http_lua_ffi_ssl_set_der_certificate(void *r,\n        const char *data, size_t len, char **err);\n\n    int ngx_http_lua_ffi_ssl_set_der_private_key(void *r,\n        const char *data, size_t len, char **err);\n\n    int ngx_http_lua_ffi_ssl_clear_certs(void *r, char **err);\n\n    void *ngx_http_lua_ffi_parse_pem_cert(const unsigned char *pem,\n        size_t pem_len, char **err);\n\n    void *ngx_http_lua_ffi_parse_pem_priv_key(const unsigned char *pem,\n        size_t pem_len, char **err);\n\n    void *ngx_http_lua_ffi_parse_der_cert(const char *data, size_t len,\n        char **err);\n\n    void *ngx_http_lua_ffi_parse_der_priv_key(const char *data, size_t len,\n        char **err);\n\n    int ngx_http_lua_ffi_set_cert(void *r,\n        void *cdata, char **err);\n\n    int ngx_http_lua_ffi_set_priv_key(void *r,\n        void *cdata, char **err);\n\n    void *ngx_http_lua_ffi_get_req_ssl_pointer(void *r);\n\n    void ngx_http_lua_ffi_free_cert(void *cdata);\n\n    void ngx_http_lua_ffi_free_priv_key(void *cdata);\n\n    int ngx_http_lua_ffi_ssl_verify_client(void *r, void *cdata,\n        void *cdata, int depth, char **err);\n\n    int ngx_http_lua_ffi_ssl_client_random(ngx_http_request_t *r,\n        unsigned char *out, size_t *outlen, char **err);\n\n    int ngx_http_lua_ffi_req_shared_ssl_ciphers(void *r, uint16_t *ciphers,\n        uint16_t *nciphers, int filter_grease, char **err);\n]]\n_EOC_\n    }\n\n    my $http_config = $block->http_config || '';\n    $http_config .= <<'_EOC_';\nlua_package_path \"$prefix/html/?.lua;../lua-resty-core/lib/?.lua;;\";\n_EOC_\n    $block->set_value(\"http_config\", $http_config);\n});\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple cert + private key\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            require \"defines\"\n            local ffi = require \"ffi\"\n\n            local errmsg = ffi.new(\"char *[1]\")\n\n            local r = require \"resty.core.base\" .get_request()\n            if r == nil then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_ssl_clear_certs(r, errmsg)\n\n            local f = assert(io.open(\"t/cert/test.crt\", \"rb\"))\n            local cert = f:read(\"*all\")\n            f:close()\n\n            local out = ffi.new(\"char [?]\", #cert)\n\n            local rc = ffi.C.ngx_http_lua_ffi_cert_pem_to_der(cert, #cert, out, errmsg)\n            if rc < 1 then\n                ngx.log(ngx.ERR, \"failed to parse PEM cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local cert_der = ffi.string(out, rc)\n\n            local rc = ffi.C.ngx_http_lua_ffi_ssl_set_der_certificate(r, cert_der, #cert_der, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to set DER cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            f = assert(io.open(\"t/cert/test.key\", \"rb\"))\n            local pkey = f:read(\"*all\")\n            f:close()\n\n            out = ffi.new(\"char [?]\", #pkey)\n\n            local rc = ffi.C.ngx_http_lua_ffi_priv_key_pem_to_der(pkey, #pkey, nil, out, errmsg)\n            if rc < 1 then\n                ngx.log(ngx.ERR, \"failed to parse PEM priv key: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local pkey_der = ffi.string(out, rc)\n\n            local rc = ffi.C.ngx_http_lua_ffi_ssl_set_der_private_key(r, pkey_der, #pkey_der, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to set DER priv key: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n        }\n\n        ssl_certificate ../../cert/test2.crt;\n        ssl_certificate_key ../../cert/test2.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 2: ECDSA cert + private key\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            local ffi = require \"ffi\"\n            require \"defines\"\n\n            local errmsg = ffi.new(\"char *[1]\")\n\n            local r = require \"resty.core.base\" .get_request()\n            if r == nil then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_ssl_clear_certs(r, errmsg)\n\n            local f = assert(io.open(\"t/cert/test_ecdsa.crt\", \"rb\"))\n            local cert = f:read(\"*all\")\n            f:close()\n\n            local out = ffi.new(\"char [?]\", #cert)\n\n            local rc = ffi.C.ngx_http_lua_ffi_cert_pem_to_der(cert, #cert, out, errmsg)\n            if rc < 1 then\n                ngx.log(ngx.ERR, \"failed to parse PEM cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local cert_der = ffi.string(out, rc)\n\n            local rc = ffi.C.ngx_http_lua_ffi_ssl_set_der_certificate(r, cert_der, #cert_der, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to set DER cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            f = assert(io.open(\"t/cert/test_ecdsa.key\", \"rb\"))\n            local pkey = f:read(\"*all\")\n            f:close()\n\n            out = ffi.new(\"char [?]\", #pkey)\n\n            local rc = ffi.C.ngx_http_lua_ffi_priv_key_pem_to_der(pkey, #pkey, nil, out, errmsg)\n            if rc < 1 then\n                ngx.log(ngx.ERR, \"failed to parse PEM priv key: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local pkey_der = ffi.string(out, rc)\n\n            local rc = ffi.C.ngx_http_lua_ffi_ssl_set_der_private_key(r, pkey_der, #pkey_der, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to set DER priv key: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n        }\n\n        ssl_certificate ../../cert/test2.crt;\n        ssl_certificate_key ../../cert/test2.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test_ecdsa.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 3: Handshake continue when cert_pem_to_der errors\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            local ffi = require \"ffi\"\n            require \"defines\"\n\n            local errmsg = ffi.new(\"char *[1]\")\n\n            local r = require \"resty.core.base\" .get_request()\n            if r == nil then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            local cert = \"garbage data\"\n\n            local out = ffi.new(\"char [?]\", #cert)\n\n            local rc = ffi.C.ngx_http_lua_ffi_cert_pem_to_der(cert, #cert, out, errmsg)\n            if rc < 1 then\n                ngx.log(ngx.ERR, \"failed to parse PEM cert: \",\n                        ffi.string(errmsg[0]))\n            end\n\n            local pkey = \"garbage key data\"\n\n            out = ffi.new(\"char [?]\", #pkey)\n\n            local rc = ffi.C.ngx_http_lua_ffi_priv_key_pem_to_der(pkey, #pkey, nil, out, errmsg)\n            if rc < 1 then\n                ngx.log(ngx.ERR, \"failed to parse PEM priv key: \",\n                        ffi.string(errmsg[0]))\n            end\n        }\n\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nfailed to parse PEM cert: PEM_read_bio_X509_AUX()\nfailed to parse PEM priv key: PEM_read_bio_PrivateKey() failed\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 4: simple cert + private key cdata\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            local ffi = require \"ffi\"\n            require \"defines\"\n\n            local errmsg = ffi.new(\"char *[1]\")\n\n            local r = require \"resty.core.base\" .get_request()\n            if r == nil then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_ssl_clear_certs(r, errmsg)\n\n            local f = assert(io.open(\"t/cert/test.crt\", \"rb\"))\n            local cert_data = f:read(\"*all\")\n            f:close()\n\n            local cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)\n            if not cert then\n                ngx.log(ngx.ERR, \"failed to parse PEM cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local rc = ffi.C.ngx_http_lua_ffi_set_cert(r, cert, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to set cdata cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_free_cert(cert)\n\n            f = assert(io.open(\"t/cert/test.key\", \"rb\"))\n            local pkey_data = f:read(\"*all\")\n            f:close()\n\n            local pkey = ffi.C.ngx_http_lua_ffi_parse_pem_priv_key(pkey_data, #pkey_data, errmsg)\n            if pkey == nil then\n                ngx.log(ngx.ERR, \"failed to parse PEM priv key: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local rc = ffi.C.ngx_http_lua_ffi_set_priv_key(r, pkey, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to set cdata priv key: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_free_priv_key(pkey)\n        }\n\n        ssl_certificate ../../cert/test2.crt;\n        ssl_certificate_key ../../cert/test2.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 5: ECDSA cert + private key cdata\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            local ffi = require \"ffi\"\n            require \"defines\"\n\n            local errmsg = ffi.new(\"char *[1]\")\n\n            local r = require \"resty.core.base\" .get_request()\n            if r == nil then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_ssl_clear_certs(r, errmsg)\n\n            local f = assert(io.open(\"t/cert/test_ecdsa.crt\", \"rb\"))\n            local cert_data = f:read(\"*all\")\n            f:close()\n\n            local cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)\n            if not cert then\n                ngx.log(ngx.ERR, \"failed to parse PEM cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local rc = ffi.C.ngx_http_lua_ffi_set_cert(r, cert, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to set cdata cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_free_cert(cert)\n\n            f = assert(io.open(\"t/cert/test_ecdsa.key\", \"rb\"))\n            local pkey_data = f:read(\"*all\")\n            f:close()\n\n            local pkey = ffi.C.ngx_http_lua_ffi_parse_pem_priv_key(pkey_data, #pkey_data, errmsg)\n            if pkey == nil then\n                ngx.log(ngx.ERR, \"failed to parse PEM priv key: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local rc = ffi.C.ngx_http_lua_ffi_set_priv_key(r, pkey, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to set cdata priv key: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_free_priv_key(pkey)\n        }\n\n        ssl_certificate ../../cert/test2.crt;\n        ssl_certificate_key ../../cert/test2.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test_ecdsa.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 6: verify client with CA certificates\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            require \"defines\"\n            local ffi = require \"ffi\"\n\n            local errmsg = ffi.new(\"char *[1]\")\n\n            local r = require \"resty.core.base\" .get_request()\n            if r == nil then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            local f = assert(io.open(\"t/cert/test.crt\", \"rb\"))\n            local cert_data = f:read(\"*all\")\n            f:close()\n\n            local client_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)\n            if not client_cert then\n                ngx.log(ngx.ERR, \"failed to parse PEM client cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, client_cert, nil, 1, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to verify client: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_free_cert(client_cert)\n        }\n\n        ssl_certificate ../../cert/test2.crt;\n        ssl_certificate_key ../../cert/test2.key;\n\n        location / {\n            default_type 'text/plain';\n            content_by_lua_block {\n                print('client certificate subject: ', ngx.var.ssl_client_s_dn)\n                ngx.say(ngx.var.ssl_client_verify)\n            }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                  https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_certificate       ../../cert/test.crt;\n        proxy_ssl_certificate_key   ../../cert/test.key;\n        proxy_ssl_session_reuse     off;\n    }\n\n--- request\nGET /t\n--- response_body\nSUCCESS\n\n--- error_log\nclient certificate subject: emailAddress=agentzh@gmail.com,CN=test.com\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 7: verify client without CA certificates\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            require \"defines\"\n            local ffi = require \"ffi\"\n\n            local errmsg = ffi.new(\"char *[1]\")\n\n            local r = require \"resty.core.base\" .get_request()\n            if r == nil then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, nil, nil, -1, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to verify client: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n        }\n\n        ssl_certificate ../../cert/test2.crt;\n        ssl_certificate_key ../../cert/test2.key;\n\n        location / {\n            default_type 'text/plain';\n            content_by_lua_block {\n                print('client certificate subject: ', ngx.var.ssl_client_s_dn)\n                ngx.say(ngx.var.ssl_client_verify)\n            }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                  https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_certificate       ../../cert/test.crt;\n        proxy_ssl_certificate_key   ../../cert/test.key;\n        proxy_ssl_session_reuse     off;\n    }\n\n--- request\nGET /t\n--- response_body eval\nqr/FAILED:self[- ]signed certificate/\n\n--- error_log\nclient certificate subject: emailAddress=agentzh@gmail.com,CN=test.com\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 8: verify client but client provides no certificate\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            require \"defines\"\n            local ffi = require \"ffi\"\n\n            local errmsg = ffi.new(\"char *[1]\")\n\n            local r = require \"resty.core.base\" .get_request()\n            if r == nil then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            local f = assert(io.open(\"t/cert/test.crt\", \"rb\"))\n            local cert_data = f:read(\"*all\")\n            f:close()\n\n            local client_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)\n            if not client_cert then\n                ngx.log(ngx.ERR, \"failed to parse PEM client cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, client_cert, nil, 1, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to verify client: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_free_cert(client_cert)\n        }\n\n        ssl_certificate ../../cert/test2.crt;\n        ssl_certificate_key ../../cert/test2.key;\n\n        location / {\n            default_type 'text/plain';\n            content_by_lua_block {\n                print('client certificate subject: ', ngx.var.ssl_client_s_dn)\n                ngx.say(ngx.var.ssl_client_verify)\n            }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                  https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_session_reuse     off;\n    }\n\n--- request\nGET /t\n--- response_body\nNONE\n\n--- error_log\nclient certificate subject: nil\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 9: simple cert + private key with passphrase\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            local ffi = require \"ffi\"\n\n            ffi.cdef[[\n                int ngx_http_lua_ffi_cert_pem_to_der(const unsigned char *pem,\n                    size_t pem_len, unsigned char *der, char **err);\n\n                int ngx_http_lua_ffi_priv_key_pem_to_der(const unsigned char *pem,\n                    size_t pem_len, const unsigned char *passphrase,\n                    unsigned char *der, char **err);\n\n                int ngx_http_lua_ffi_ssl_set_der_certificate(void *r,\n                    const char *data, size_t len, char **err);\n\n                int ngx_http_lua_ffi_ssl_set_der_private_key(void *r,\n                    const char *data, size_t len, char **err);\n\n                int ngx_http_lua_ffi_ssl_clear_certs(void *r, char **err);\n            ]]\n\n            local errmsg = ffi.new(\"char *[1]\")\n\n            local r = require \"resty.core.base\" .get_request()\n            if not r then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_ssl_clear_certs(r, errmsg)\n\n            local f = assert(io.open(\"t/cert/test_passphrase.crt\", \"rb\"))\n            local cert = f:read(\"*all\")\n            f:close()\n\n            local out = ffi.new(\"char [?]\", #cert)\n\n            local rc = ffi.C.ngx_http_lua_ffi_cert_pem_to_der(cert, #cert, out, errmsg)\n            if rc < 1 then\n                ngx.log(ngx.ERR, \"failed to parse PEM cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local cert_der = ffi.string(out, rc)\n\n            local rc = ffi.C.ngx_http_lua_ffi_ssl_set_der_certificate(r, cert_der, #cert_der, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to set DER cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            f = assert(io.open(\"t/cert/test_passphrase.key\", \"rb\"))\n            local pkey = f:read(\"*all\")\n            f:close()\n\n            local passphrase = \"123456\"\n\n            out = ffi.new(\"char [?]\", #pkey)\n\n            local rc = ffi.C.ngx_http_lua_ffi_priv_key_pem_to_der(pkey, #pkey, passphrase, out, errmsg)\n            if rc < 1 then\n                ngx.log(ngx.ERR, \"failed to parse PEM priv key: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local pkey_der = ffi.string(out, rc)\n\n            local rc = ffi.C.ngx_http_lua_ffi_ssl_set_der_private_key(r, pkey_der, #pkey_der, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to set DER priv key: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n        }\n\n        ssl_certificate ../../cert/test2.crt;\n        ssl_certificate_key ../../cert/test2.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test_passphrase.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to recieve response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 10: Raw SSL pointer\n--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            local ffi = require \"ffi\"\n            require \"defines\"\n\n            local r = require \"resty.core.base\" .get_request()\n            if not r then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            local ssl = ffi.C.ngx_http_lua_ffi_get_req_ssl_pointer(r);\n            if ssl == nil then\n                ngx.log(ngx.ERR, \"failed to retrieve SSL*\")\n                return\n            end\n\n            ffi.cdef[[\n                const char *SSL_get_servername(const void *, const int);\n            ]]\n            local TLSEXT_NAMETYPE_host_name = 0\n            local sni = ffi.C.SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)\n            if sni == nil then\n                ngx.log(ngx.ERR, \"failed to get sni\")\n                return\n            end\n\n            ngx.log(ngx.INFO, \"SNI is \", ffi.string(sni))\n        }\n\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nSNI is test.com\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 11: DER cert + private key cdata\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            local ffi = require \"ffi\"\n            require \"defines\"\n\n            local errmsg = ffi.new(\"char *[1]\")\n\n            local r = require \"resty.core.base\" .get_request()\n            if r == nil then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_ssl_clear_certs(r, errmsg)\n\n            local f = assert(io.open(\"t/cert/test_der.crt\", \"rb\"))\n            local cert_data = f:read(\"*all\")\n            f:close()\n\n            local cert = ffi.C.ngx_http_lua_ffi_parse_der_cert(cert_data, #cert_data, errmsg)\n            if not cert then\n                ngx.log(ngx.ERR, \"failed to parse DER cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local rc = ffi.C.ngx_http_lua_ffi_set_cert(r, cert, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to set cdata cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_free_cert(cert)\n\n            f = assert(io.open(\"t/cert/test_der.key\", \"rb\"))\n            local pkey_data = f:read(\"*all\")\n            f:close()\n\n            local pkey = ffi.C.ngx_http_lua_ffi_parse_der_priv_key(pkey_data, #pkey_data, errmsg)\n            if pkey == nil then\n                ngx.log(ngx.ERR, \"failed to parse DER priv key: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local rc = ffi.C.ngx_http_lua_ffi_set_priv_key(r, pkey, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to set cdata priv key: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_free_priv_key(pkey)\n        }\n\n        ssl_certificate ../../cert/test2.crt;\n        ssl_certificate_key ../../cert/test2.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 12: client random\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            local ffi = require \"ffi\"\n            require \"defines\"\n\n            local errmsg = ffi.new(\"char *[1]\")\n\n            local r = require \"resty.core.base\" .get_request()\n            if r == nil then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            -- test client random length\n            local out = ffi.new(\"unsigned char[?]\", 0)\n            local sizep = ffi.new(\"size_t[1]\", 0)\n\t\t\t\n            local rc = ffi.C.ngx_http_lua_ffi_ssl_client_random(r, out, sizep, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to get client random length: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            if tonumber(sizep[0]) ~= 32 then\n                ngx.log(ngx.ERR, \"client random length does not equal 32\")\n                return\n            end\n\n            -- test client random value\n            out = ffi.new(\"unsigned char[?]\", 50)\n            sizep = ffi.new(\"size_t[1]\", 50)\n\n            rc = ffi.C.ngx_http_lua_ffi_ssl_client_random(r, out, sizep, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to get client random: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            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\"\n            if ffi.string(out, sizep[0]) == init_v then\n                ngx.log(ngx.ERR, \"maybe the client random value is incorrect\")\n                return\n            end\n        }\n\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 13: verify client, but server don't trust root ca\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   example.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            require \"defines\"\n            local ffi = require \"ffi\"\n\n            local errmsg = ffi.new(\"char *[1]\")\n\n            local r = require \"resty.core.base\" .get_request()\n            if r == nil then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            local f = assert(io.open(\"t/cert/mtls_server.crt\", \"rb\"))\n            local cert_data = f:read(\"*all\")\n            f:close()\n\n            local client_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)\n            if not client_cert then\n                ngx.log(ngx.ERR, \"failed to parse PEM client cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, client_cert, nil, 2, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to verify client: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_free_cert(client_cert)\n        }\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n            content_by_lua_block {\n                ngx.say(ngx.var.ssl_client_verify)\n            }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                  https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_certificate       ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key   ../../cert/mtls_client.key;\n        proxy_ssl_session_reuse     off;\n    }\n\n--- request\nGET /t\n--- response_body\nFAILED:unable to verify the first certificate\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 14: verify client and server trust root ca\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   example.com;\n\n        ssl_certificate_by_lua_block {\n            collectgarbage()\n\n            require \"defines\"\n            local ffi = require \"ffi\"\n\n            local errmsg = ffi.new(\"char *[1]\")\n\n            local r = require \"resty.core.base\" .get_request()\n            if r == nil then\n                ngx.log(ngx.ERR, \"no request found\")\n                return\n            end\n\n            local f = assert(io.open(\"t/cert/mtls_server.crt\", \"rb\"))\n            local cert_data = f:read(\"*all\")\n            f:close()\n\n            local client_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)\n            if not client_cert then\n                ngx.log(ngx.ERR, \"failed to parse PEM client cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local f = assert(io.open(\"t/cert/mtls_ca.crt\", \"rb\"))\n            local cert_data = f:read(\"*all\")\n            f:close()\n\n            local trusted_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)\n            if not trusted_cert then\n                ngx.log(ngx.ERR, \"failed to parse PEM trusted cert: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, cert, trusted_cert, 2, errmsg)\n            if rc ~= 0 then\n                ngx.log(ngx.ERR, \"failed to verify client: \",\n                        ffi.string(errmsg[0]))\n                return\n            end\n\n            ffi.C.ngx_http_lua_ffi_free_cert(client_cert)\n            ffi.C.ngx_http_lua_ffi_free_cert(trusted_cert)\n        }\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n            content_by_lua_block {\n                ngx.say(ngx.var.ssl_client_verify)\n            }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                  https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_certificate       ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key   ../../cert/mtls_client.key;\n        proxy_ssl_session_reuse     off;\n    }\n\n--- request\nGET /t\n--- response_body\nSUCCESS\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 15: Get supported ciphers\n--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        ssl_protocols TLSv1.2;\n        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;\n\n        server_tokens off;  \n        \n        location /ciphers { \n            content_by_lua_block {\n                require \"defines\"\n                local ffi = require \"ffi\"\n                local cjson = require \"cjson.safe\"\n                local base = require \"resty.core.base\"\n                local get_request = base.get_request\n\n                local MAX_CIPHERS = 64\n                local ciphers = ffi.new(\"uint16_t[?]\", MAX_CIPHERS)\n                local nciphers = ffi.new(\"uint16_t[1]\", MAX_CIPHERS)\n                local err = ffi.new(\"char*[1]\")\n\n                local r = get_request()\n                local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err)\n\n                if ret ~= 0 then\n                    ngx.log(ngx.ERR, \"error: \", ffi.string(err[0]))\n                    return\n                end\n\n                local res = {}\n                for i = 0, nciphers[0] - 1 do\n                    local cipher_id = string.format(\"%04x\", ciphers[i])\n                    table.insert(res, cipher_id)\n                end\n\n                ngx.say(cjson.encode(res))\n            }\n        }\n    }\n--- config\n    server_tokens off;\n    location /t {\n        proxy_ssl_protocols TLSv1.2;\n        proxy_ssl_session_reuse     off;        \n        proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256;\n        proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers;\n    }\n--- request\nGET /t\n--- response_body_like\n\\[\"c02f\",\"c02b\"\\]\n--- error_log chomp\nTLSv1.2, cipher: \"ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD\"\n\n\n\n=== TEST 16: SSL cipher API error handling (no SSL)\n--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}\n--- config\n    location /t {\n        content_by_lua_block {\n            require \"defines\"        \n            local ffi = require \"ffi\"\n            \n            local ciphers = ffi.new(\"uint16_t[64]\")\n            local nciphers = ffi.new(\"uint16_t[1]\", 64)\n            local err = ffi.new(\"char*[1]\")\n\n            -- use nil request to trigger error\n            local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(nil, ciphers, nciphers, 0, err)\n\n            ngx.say(\"ret: \", ret)\n            if err[0] ~= nil then\n                ngx.say(\"err: \", ffi.string(err[0]))\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\nret: -1\nerr: bad request\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 17: Buffer overflow handling\n--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        ssl_protocols TLSv1.2;\n        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;\n\n        server_tokens off;  \n\n        \n        location /ciphers { \n            content_by_lua_block {\n                require \"defines\"            \n                local ffi = require \"ffi\"\n                local base = require \"resty.core.base\"\n                local get_request = base.get_request\n                local cjson = require \"cjson.safe\"\n    \n                local MAX_CIPHERS = 64\n                local ciphers = ffi.new(\"uint16_t[?]\", MAX_CIPHERS)\n                local nciphers = ffi.new(\"uint16_t[1]\", MAX_CIPHERS)\n                local err = ffi.new(\"char*[1]\")\n\n                local r = get_request()\n                local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err)\n\n                if ret ~= 0 then\n                    ngx.log(ngx.ERR, \"error: \", ffi.string(err[0]))\n                    return\n                end\n                local res = {}\n                for i = 0, nciphers[0] - 1 do\n                    local cipher_id = string.format(\"%04x\", ciphers[i])\n\n                    table.insert(res, cipher_id)\n                end\n                ngx.say(cjson.encode(res))\n            }\n        }\n    }\n--- config\n    server_tokens off;\n    location /t {\n        proxy_ssl_protocols TLSv1.2;\n        proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256;\n        proxy_ssl_session_reuse off;    \n        proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers;\n    }\n--- request\nGET /t\n--- response_body_like\n\\[\"c02f\"\\]\n--- error_code: 200\n--- error_log chomp\nTLSv1.2, cipher: \"ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD\"\n\n\n\n=== TEST 18: BORINGSSL error handling\n--- skip_eval: 8:$ENV{TEST_NGINX_USE_OPENSSL}\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        ssl_protocols TLSv1.2 TLSv1.3;\n        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;\n\n        server_tokens off;  \n\n        \n        location /ciphers { \n            content_by_lua_block {\n                require \"defines\"            \n                local ffi = require \"ffi\"\n                local base = require \"resty.core.base\"\n                local get_request = base.get_request\n\n                local MAX_CIPHERS = 64\n                local ciphers = ffi.new(\"uint16_t[?]\", MAX_CIPHERS)\n                local nciphers = ffi.new(\"uint16_t[1]\", MAX_CIPHERS)\n                local err = ffi.new(\"char*[1]\")\n\n                local r = get_request()\n                local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err)\n\n                if ret ~= 0 then\n                    ngx.say(\"Error: \", ffi.string(err[0]))\n                    return\n                end\n            \n            }\n        }              \n    }\n--- config\n    server_tokens off;\n    location /t {\n        proxy_ssl_protocols TLSv1.2 TLSv1.3;\n        proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256;\n        proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers;\n    }\n--- request\nGET /t\n--- response_body_like chomp\nError: BoringSSL is not supported for SSL cipher operations\n--- error_code: 200\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 19: Get supported ciphers with GREASE filtering\n--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        ssl_protocols TLSv1.2;\n        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;\n\n        server_tokens off;\n        \n        location /ciphers {\n            content_by_lua_block {\n                require \"defines\"\n                local ffi = require \"ffi\"\n                local cjson = require \"cjson.safe\"\n                local base = require \"resty.core.base\"\n                local get_request = base.get_request\n\n                local MAX_CIPHERS = 64\n                local ciphers = ffi.new(\"uint16_t[?]\", MAX_CIPHERS)\n                local nciphers = ffi.new(\"uint16_t[1]\", MAX_CIPHERS)\n                local err = ffi.new(\"char*[1]\")\n\n                local r = get_request()\n                -- Test without GREASE filtering\n                local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err)\n                if ret ~= 0 then\n                    ngx.log(ngx.ERR, \"error without filtering: \", ffi.string(err[0]))\n                    return\n                end\n\n                local res_no_filter = {}\n                for i = 0, nciphers[0] - 1 do\n                    local cipher_id = string.format(\"%04x\", ciphers[i])\n                    table.insert(res_no_filter, cipher_id)\n                end\n\n                -- Reset buffers\n                nciphers[0] = MAX_CIPHERS\n                \n                -- Test with GREASE filtering\n                local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 1, err)\n                if ret ~= 0 then\n                    ngx.log(ngx.ERR, \"error with filtering: \", ffi.string(err[0]))\n                    return\n                end\n\n                local res_with_filter = {}\n                for i = 0, nciphers[0] - 1 do\n                    local cipher_id = string.format(\"%04x\", ciphers[i])\n                    table.insert(res_with_filter, cipher_id)\n                end\n\n                ngx.say(\"without_filter:\", cjson.encode(res_no_filter))\n                ngx.say(\"with_filter:\", cjson.encode(res_with_filter))\n            }\n        }\n    }\n--- config\n    server_tokens off;\n    location /t {\n        proxy_ssl_protocols TLSv1.2;\n        proxy_ssl_session_reuse     off;\n        proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256;\n        proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers;\n    }\n--- request\nGET /t\n--- response_body_like\nwithout_filter:\\[.*\\]\nwith_filter:\\[.*\\]\n--- error_log chomp\nTLSv1.2, cipher: \"ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD\"\n"
  },
  {
    "path": "t/141-luajit.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua\n    skip_all => 'no mmap(sbrk(0)) trick since glibc leaks memory in this case';\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: avoid the data segment from growing on Linux\nThis is to maximize the address space that can be used by LuaJIT.\n--- config\n    location = /t {\n        content_by_lua_block {\n            local ffi = require \"ffi\"\n            ffi.cdef[[\n                void *malloc(size_t size);\n                void free(void *p);\n            ]]\n            local p = ffi.C.malloc(1);\n            local num = tonumber(ffi.cast(\"uintptr_t\", p))\n            ffi.C.free(p)\n            if ffi.abi(\"64bit\") then\n                if num < 2^31 then\n                    ngx.say(\"fail: \", string.format(\"p = %#x\", num))\n                    return\n                end\n            end\n            ngx.say(\"pass\")\n        }\n    }\n--- request\nGET /t\n--- response_body\npass\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/142-ssl-session-store.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse Cwd qw(abs_path realpath);\nuse File::Basename;\n\nrepeat_each(3);\n\nplan tests => repeat_each() * (blocks() * 6 - 1);\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_CERT_DIR} ||= dirname(realpath(abs_path(__FILE__)));\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple logging\n--- http_config\n    ssl_session_store_by_lua_block { print(\"ssl session store by lua is running!\") }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\n\n--- no_error_log\n[error]\n[alert]\n--- grep_error_log eval: qr/ssl_session_store_by_lua\\(nginx.conf:\\d+\\):.*?,|\\bssl session store: connection reusable: \\d+|\\breusable connection: \\d+/\n--- grep_error_log_out eval\nqr/^reusable connection: 0\nssl session store: connection reusable: 0\nssl_session_store_by_lua\\(nginx\\.conf:25\\):1: ssl session store by lua is running!,\n/m,\n\n\n\n=== TEST 2: sleep is not allowed\n--- http_config\n    ssl_session_store_by_lua_block {\n        local begin = ngx.now()\n        ngx.sleep(0.1)\n        print(\"elapsed in ssl store session by lua: \", ngx.now() - begin)\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nAPI disabled in the context of ssl_session_store_by_lua*\n\n--- no_error_log\n[alert]\n[emerg]\n\n\n\n=== TEST 3: timer\n--- http_config\n    ssl_session_store_by_lua_block {\n        local function f()\n            print(\"my timer run!\")\n        end\n        local ok, err = ngx.timer.at(0, f)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to create timer: \", err)\n            return\n        end\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nmy timer run!\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 4: cosocket is not allowed\n--- http_config\n    ssl_session_store_by_lua_block {\n        local sock = ngx.socket.tcp()\n\n        sock:settimeout(5000)\n\n        local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to connect to memc: \", err)\n            return\n        end\n\n        local bytes, err = sock:send(\"flush_all\\\\r\\\\n\")\n        if not bytes then\n            ngx.log(ngx.ERR, \"failed to send flush_all command: \", err)\n            return\n        end\n\n        local res, err = sock:receive()\n        if not res then\n            ngx.log(ngx.ERR, \"failed to receive memc reply: \", err)\n            return\n        end\n\n        print(\"received memc reply: \", res)\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nAPI disabled in the context of ssl_session_store_by_lua*\n\n--- no_error_log\n[alert]\n[emerg]\n\n\n\n=== TEST 5: ngx.exit(0) - no yield\n--- http_config\n    ssl_session_store_by_lua_block {\n        ngx.exit(0)\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_verify_depth 3;\n    lua_ssl_protocols TLSv1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- error_log\nlua exit with code 0\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 6: ngx.exit(ngx.ERROR) - no yield\nngx.exit does not yield and the error code is eaten.\n--- http_config\n    ssl_session_store_by_lua_block {\n        ngx.exit(ngx.ERROR)\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_verify_depth 3;\n    lua_ssl_protocols TLSv1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- error_log\nlua exit with code -1\nssl_session_store_by_lua*: handler return value: 0, sess new cb exit code: 0\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 7: lua exception - no yield\n--- http_config\n    ssl_session_store_by_lua_block {\n        error(\"bad bad bad\")\n        ngx.log(ngx.ERR, \"should never reached here...\")\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_verify_depth 3;\n    lua_ssl_protocols TLSv1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- error_log\nfailed to run session_store_by_lua*: ssl_session_store_by_lua(nginx.conf:25):2: bad bad bad\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 8: get phase\n--- http_config\n    ssl_session_store_by_lua_block {\n        print(\"get_phase: \", ngx.get_phase())\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_verify_depth 3;\n    lua_ssl_protocols TLSv1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- error_log\nget_phase: ssl_session_store\n\n--- no_error_log\n[alert]\n[emerg]\n[error]\n\n\n\n=== TEST 9: inter-operation with ssl_certificate_by_lua\n--- http_config\n    ssl_session_store_by_lua_block { print(\"ssl store session by lua is running!\") }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate_by_lua_block {\n            local begin = ngx.now()\n            ngx.sleep(0.1)\n            print(\"elapsed in ssl cert by lua: \", ngx.now() - begin)\n        }\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- error_log eval\n[\n'lua ssl server name: \"test.com\"',\nqr/elapsed in ssl cert by lua: 0.(?:09|1[01])\\d+,/,\n'ssl_session_store_by_lua(nginx.conf:25):1: ssl store session by lua is running!',\n]\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 10: simple logging (by file)\n--- http_config\n    ssl_session_store_by_lua_file html/a.lua;\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- user_files\n>>> a.lua\nprint(\"ssl store session by lua is running!\")\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\na.lua:1: ssl store session by lua is running!\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 11: will crash when ssl_session_store_by_lua* is allowed in server context\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name foo.com;\n        ssl_session_store_by_lua_block {\n            print(\"handler in test.com\")\n        }\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n\n        server_tokens off;\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_verify_depth 3;\n    lua_ssl_protocols TLSv1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- no_error_log\n[error]\n--- must_die\n--- error_log eval\nqr/\\[emerg\\] .*? \"ssl_session_store_by_lua_block\" directive is not allowed here .*?\\bnginx\\.conf:28/\n\n\n\n=== TEST 12: mixing ssl virtual servers with non-ssl virtual servers\n--- http_config\n    ssl_session_store_by_lua_block { print(\"ssl session store by lua is running!\") }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/https.sock ssl;\n        server_name   test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/http.sock;\n        server_name   foo.com;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/https.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nssl_session_store_by_lua(nginx.conf:25):1: ssl session store by lua is running!\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 13: ssl_session_store_by_lua* is skipped when using TLSv1.3\n--- skip_openssl: 6: < 1.1.1\n--- http_config\n    ssl_session_store_by_lua_block { ngx.log(ngx.ERR, \"ssl_session_store_by_lua* is running!\") }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n        ssl_protocols TLSv1.3;\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1.3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n--- error_log eval\nqr/ssl_session_store_by_lua\\*: skipped since TLS version >= 1\\.3 \\(\\d+\\)/\n--- no_error_log\n[error]\n[alert]\n[emerg]\n--- skip_eval: 6:$ENV{TEST_NGINX_USE_HTTP3}\n"
  },
  {
    "path": "t/143-ssl-session-fetch.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse lib 'lib';\nuse Test::Nginx::Socket::Lua;\nuse Cwd qw(abs_path realpath);\nuse File::Basename;\n\nrepeat_each(3);\n\nplan tests => repeat_each() * (blocks() * 6) - 3;\n\nmy $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';\nmy $openssl_version = eval { `$NginxBinary -V 2>&1` };\nif ($openssl_version =~ m/BoringSSL/) {\n    $ENV{TEST_NGINX_USE_BORINGSSL} = 1;\n}\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_CERT_DIR} ||= dirname(realpath(abs_path(__FILE__)));\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple logging\n--- http_config\n    ssl_session_fetch_by_lua_block { print(\"ssl fetch sess by lua is running!\") }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- grep_error_log eval: qr/ssl_session_fetch_by_lua\\(nginx\\.conf:\\d+\\):.*?,|\\bssl session fetch: connection reusable: \\d+|\\breusable connection: \\d+/\n\n--- grep_error_log_out eval\n# Since nginx version 1.17.9, nginx call ngx_reusable_connection(c, 0)\n# before call ssl callback function\n$Test::Nginx::Util::NginxVersion >= 1.017009 ?\n[\nqr/\\A(?:reusable connection: [01]\\n)+\\z/s,\nqr/^reusable connection: 0\nssl session fetch: connection reusable: 0\nssl_session_fetch_by_lua\\(nginx\\.conf:25\\):1: ssl fetch sess by lua is running!,\n/m,\nqr/^reusable connection: 0\nssl session fetch: connection reusable: 0\nssl_session_fetch_by_lua\\(nginx\\.conf:25\\):1: ssl fetch sess by lua is running!,\n/m,\n]\n:\n[\nqr/\\A(?:reusable connection: [01]\\n)+\\z/s,\nqr/^reusable connection: 1\nssl session fetch: connection reusable: 1\nreusable connection: 0\nssl_session_fetch_by_lua\\(nginx\\.conf:25\\):1: ssl fetch sess by lua is running!,\n/m,\nqr/^reusable connection: 1\nssl session fetch: connection reusable: 1\nreusable connection: 0\nssl_session_fetch_by_lua\\(nginx\\.conf:25\\):1: ssl fetch sess by lua is running!,\n/m,\n]\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 2: sleep\n--- http_config\n    ssl_session_fetch_by_lua_block {\n        local begin = ngx.now()\n        ngx.sleep(0.1)\n        print(\"elapsed in ssl fetch session by lua: \", ngx.now() - begin)\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- skip_eval: 6:$ENV{TEST_NGINX_USE_BORINGSSL}\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2 TLSv1.3;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- grep_error_log eval\nqr/elapsed in ssl fetch session by lua: 0.(?:09|1[01])\\d+,/,\n\n--- grep_error_log_out eval\n[\n'',\nqr/elapsed in ssl fetch session by lua: 0.(?:09|1[01])\\d+,/,\nqr/elapsed in ssl fetch session by lua: 0.(?:09|1[01])\\d+,/,\n]\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n--- skip_openssl: 6: > 1.1.1w\n\n\n\n=== TEST 3: timer\n--- http_config\n    ssl_session_fetch_by_lua_block {\n        local function f()\n            print(\"my timer run!\")\n        end\n        local ok, err = ngx.timer.at(0, f)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to create timer: \", err)\n            return\n        end\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- grep_error_log eval\nqr/my timer run!/s\n\n--- grep_error_log_out eval\n[\n'',\n'my timer run!\n',\n'my timer run!\n',\n]\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 4: cosocket\n--- http_config\n    ssl_session_fetch_by_lua_block {\n        local sock = ngx.socket.tcp()\n\n        sock:settimeout(5000)\n\n        local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to connect to memc: \", err)\n            return\n        end\n\n        local bytes, err = sock:send(\"flush_all\\r\\n\")\n        if not bytes then\n            ngx.log(ngx.ERR, \"failed to send flush_all command: \", err)\n            return\n        end\n\n        local res, err = sock:receive()\n        if not res then\n            ngx.log(ngx.ERR, \"failed to receive memc reply: \", err)\n            return\n        end\n\n        print(\"received memc reply: \", res)\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- grep_error_log eval\nqr/received memc reply: OK/s\n\n--- grep_error_log_out eval\n[\n'',\n'received memc reply: OK\n',\n'received memc reply: OK\n',\n]\n\n--- no_error_log\n[alert]\n[error]\n[emerg]\n--- skip_openssl: 6: > 1.1.1w\n\n\n\n=== TEST 5: ngx.exit(0) - yield\n--- http_config\n    ssl_session_fetch_by_lua_block {\n        ngx.exit(0)\n        ngx.log(ngx.ERR, \"should never reached here...\")\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_verify_depth 3;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- grep_error_log eval\nqr/lua exit with code 0/s\n\n--- grep_error_log_out eval\n[\n'',\n'lua exit with code 0\n',\n'lua exit with code 0\n',\n]\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 6: ngx.exit(ngx.ERROR) - yield\n--- http_config\n    ssl_session_fetch_by_lua_block {\n        ngx.exit(ngx.ERROR)\n        ngx.log(ngx.ERR, \"should never reached here...\")\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_verify_depth 3;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- grep_error_log eval\nqr/ssl_session_fetch_by_lua\\*: handler return value: -1, sess get cb exit code: 0/s\n\n--- grep_error_log_out eval\n[\n'',\n'ssl_session_fetch_by_lua*: handler return value: -1, sess get cb exit code: 0\n',\n'ssl_session_fetch_by_lua*: handler return value: -1, sess get cb exit code: 0\n',\n]\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 7: ngx.exit(ngx.ERROR) - yield\n--- http_config\n    ssl_session_fetch_by_lua_block {\n        ngx.sleep(0.001)\n        ngx.exit(ngx.ERROR)\n        ngx.log(ngx.ERR, \"should never reached here...\")\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_verify_depth 3;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- grep_error_log eval\nqr/ssl_session_fetch_by_lua\\*: sess get cb exit code: 0/s\n\n--- grep_error_log_out eval\n[\n'',\n'ssl_session_fetch_by_lua*: sess get cb exit code: 0\n',\n'ssl_session_fetch_by_lua*: sess get cb exit code: 0\n',\n]\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n--- skip_openssl: 6: > 1.1.1w\n\n\n\n=== TEST 8: lua exception - no yield\n--- http_config\n    ssl_session_fetch_by_lua_block {\n        error(\"bad bad bad\")\n        ngx.log(ngx.ERR, \"should never reached here...\")\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- grep_error_log eval\nqr/ssl_session_fetch_by_lua\\(nginx.conf:\\d+\\):2: bad bad bad/s\n\n--- grep_error_log_out eval\n[\n'',\n'ssl_session_fetch_by_lua(nginx.conf:25):2: bad bad bad\n',\n'ssl_session_fetch_by_lua(nginx.conf:25):2: bad bad bad\n',\n\n]\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 9: lua exception - yield\n--- http_config\n    ssl_session_fetch_by_lua_block {\n        ngx.sleep(0.001)\n        error(\"bad bad bad\")\n        ngx.log(ngx.ERR, \"should never reached here...\")\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- grep_error_log eval\nqr/ssl_session_fetch_by_lua\\(nginx.conf:\\d+\\):3: bad bad bad|ssl_session_fetch_by_lua\\*: sess get cb exit code: 0/s\n\n--- grep_error_log_out eval\n[\n'',\n'ssl_session_fetch_by_lua(nginx.conf:25):3: bad bad bad\nssl_session_fetch_by_lua*: sess get cb exit code: 0\n',\n'ssl_session_fetch_by_lua(nginx.conf:25):3: bad bad bad\nssl_session_fetch_by_lua*: sess get cb exit code: 0\n',\n\n]\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n--- skip_openssl: 6: > 1.1.1w\n\n\n\n=== TEST 10: get phase\n--- http_config\n    ssl_session_fetch_by_lua_block { print(\"get_phase: \", ngx.get_phase()) }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- grep_error_log eval\nqr/get_phase: ssl_session_fetch/s\n\n--- grep_error_log_out eval\n[\n'',\n'get_phase: ssl_session_fetch\n',\n'get_phase: ssl_session_fetch\n',\n]\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 11: inter-operation with ssl_certificate_by_lua\n--- http_config\n    ssl_session_store_by_lua_block { print(\"ssl store session by lua is running!\") }\n    ssl_session_fetch_by_lua_block {\n        ngx.sleep(0.1)\n        print(\"ssl fetch session by lua is running!\")\n    }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate_by_lua_block {\n            ngx.sleep(0.1)\n            print(\"ssl cert by lua is running!\")\n        }\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- grep_error_log eval\nqr/ssl ((fetch|store) session|cert) by lua is running!/s\n\n--- grep_error_log_out eval\nif ($ENV{TEST_NGINX_USE_HTTP3}) {\n[\n'ssl cert by lua is running!\nssl store session by lua is running!\n',\n'ssl cert by lua is running!\nssl fetch session by lua is running!\nssl store session by lua is running!\n',\n'ssl cert by lua is running!\nssl fetch session by lua is running!\nssl store session by lua is running!\n',\n]\n} else {\n[\n'ssl cert by lua is running!\nssl store session by lua is running!\n',\n'ssl fetch session by lua is running!\nssl cert by lua is running!\nssl store session by lua is running!\n',\n'ssl fetch session by lua is running!\nssl cert by lua is running!\nssl store session by lua is running!\n',\n]\n}\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n--- skip_openssl: 6: > 1.1.1w\n\n\n\n=== TEST 12: simple logging (by file)\n--- http_config\n    ssl_session_fetch_by_lua_file html/a.lua;\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- user_files\n>>> a.lua\nprint(\"ssl fetch sess by lua is running!\")\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- grep_error_log eval\nqr/\\S+:\\d+: ssl fetch sess by lua is running!/s\n\n--- grep_error_log_out eval\n[\n'',\n'a.lua:1: ssl fetch sess by lua is running!\n',\n'a.lua:1: ssl fetch sess by lua is running!\n',\n]\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 13: mixing ssl virtual servers with non-ssl virtual servers\n--- http_config\n    ssl_session_fetch_by_lua_block { print(\"ssl fetch sess by lua is running!\") }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/http.sock;\n        server_name   foo.com;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n\n--- grep_error_log eval\nqr/ssl_session_fetch_by_lua\\(nginx.conf:\\d+\\):1: ssl fetch sess by lua is running!/s\n\n--- grep_error_log_out eval\n[\n'',\n'ssl_session_fetch_by_lua(nginx.conf:25):1: ssl fetch sess by lua is running!\n',\n'ssl_session_fetch_by_lua(nginx.conf:25):1: ssl fetch sess by lua is running!\n',\n]\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 14: keep global variable in ssl_session_(store|fetch)_by_lua when OpenResty LuaJIT is used\n--- http_config\n    ssl_session_store_by_lua_block {\n        ngx.log(ngx.WARN, \"new foo: \", foo)\n        if not foo then\n            foo = 1\n        else\n            ngx.log(ngx.WARN, \"old foo: \", foo)\n            foo = foo + 1\n        end\n    }\n    ssl_session_fetch_by_lua_block {\n        ngx.log(ngx.WARN, \"new bar: \", foo)\n        if not bar then\n            bar = 1\n        else\n            ngx.log(ngx.WARN, \"old bar: \", bar)\n            bar = bar + 1\n        end\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        ssl_session_tickets off;\n\n        server_tokens off;\n        location /foo {\n            content_by_lua_block {\n                ngx.say(\"foo: \", foo)\n                ngx.say(\"bar: \", bar)\n            }\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                package.loaded.session = sess\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    local m, err = ngx.re.match(line, \"^foo: (.*)$\", \"jo\")\n                    if err then\n                        ngx.say(\"failed to match line: \", err)\n                    end\n\n                    if m and m[1] then\n                        ngx.print(m[1])\n                    end\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"done\")\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body_like chomp\n\\A[123]done\\n\\z\n--- grep_error_log eval: qr/old (foo|bar): \\d+/\n--- grep_error_log_out eval\n[\"\", \"old foo: 1\\n\", \"old bar: 1\\nold foo: 2\\n\"]\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 15: ssl_session_fetch_by_lua* is skipped when session ticket is provided\n--- http_config\n    ssl_session_fetch_by_lua_block { ngx.log(ngx.ERR, \"ssl_session_fetch_by_lua* is running!\") }\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 16: ssl_session_fetch_by_lua* can yield when reading early data\n--- skip_openssl: 6: < 1.1.1\n--- http_config\n    ssl_session_fetch_by_lua_block {\n        local begin = ngx.now()\n        ngx.sleep(0.1)\n        print(\"elapsed in ssl_session_fetch_by_lua*: \", ngx.now() - begin)\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n        ssl_early_data on;\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n--- grep_error_log eval\nqr/elapsed in ssl_session_fetch_by_lua\\*: 0\\.(?:09|1[01])\\d+,/,\n--- grep_error_log_out eval\n[\n'',\nqr/elapsed in ssl_session_fetch_by_lua\\*: 0\\.(?:09|1[01])\\d+,/,\nqr/elapsed in ssl_session_fetch_by_lua\\*: 0\\.(?:09|1[01])\\d+,/,\n]\n--- no_error_log\n[error]\n[alert]\n[emerg]\n--- skip_openssl: 6: > 1.1.1w\n\n\n\n=== TEST 17: cosocket (UDP)\n--- http_config\n    ssl_session_fetch_by_lua_block {\n        local sock = ngx.socket.udp()\n\n        sock:settimeout(1000)\n\n        local ok, err = sock:setpeername(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to connect to memc: \", err)\n            return\n        end\n\n        local req = \"\\0\\1\\0\\0\\0\\1\\0\\0flush_all\\r\\n\"\n        local ok, err = sock:send(req)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to send flush_all to memc: \", err)\n            return\n        end\n\n        local res, err = sock:receive()\n        if not res then\n            ngx.log(ngx.ERR, \"failed to receive memc reply: \", err)\n            return\n        end\n\n        ngx.log(ngx.INFO, \"received memc reply of \", #res, \" bytes\")\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n--- grep_error_log eval: qr/received memc reply of \\d+ bytes/\n--- grep_error_log_out eval\n[\n'',\n'received memc reply of 12 bytes\n',\n'received memc reply of 12 bytes\n',\n]\n--- no_error_log\n[alert]\n[error]\n[emerg]\n--- skip_openssl: 6: > 1.1.1w\n\n\n\n=== TEST 18: uthread (kill)\n--- http_config\n    ssl_session_fetch_by_lua_block {\n        local function f()\n            ngx.log(ngx.INFO, \"uthread: hello from f()\")\n            ngx.sleep(1)\n        end\n\n        local t, err = ngx.thread.spawn(f)\n        if not t then\n            ngx.log(ngx.ERR, \"failed to spawn thread: \", err)\n            return\n        end\n\n        collectgarbage()\n\n        local ok, err = ngx.thread.kill(t)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to kill thread: \", err)\n            return\n        end\n\n        ngx.log(ngx.INFO, \"uthread: killed\")\n\n        local ok, err = ngx.thread.kill(t)\n        if not ok then\n            ngx.log(ngx.INFO, \"uthread: failed to kill: \", err)\n        end\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n--- grep_error_log eval: qr/uthread: [^.,]+/\n--- grep_error_log_out eval\n[\n'',\n'uthread: hello from f()\nuthread: killed\nuthread: failed to kill: already waited or killed\n',\n'uthread: hello from f()\nuthread: killed\nuthread: failed to kill: already waited or killed\n'\n]\n--- no_error_log\n[alert]\n[error]\n[emerg]\n\n\n\n=== TEST 19: uthread (wait)\n--- http_config\n    ssl_session_fetch_by_lua_block {\n        local function f()\n            ngx.log(ngx.INFO, \"uthread: hello from f()\")\n            ngx.sleep(0.001)\n            return 32\n        end\n\n        local t, err = ngx.thread.spawn(f)\n        if not t then\n            ngx.log(ngx.ERR, \"failed to spawn thread: \", err)\n            return\n        end\n\n        collectgarbage()\n\n        local ok, res = ngx.thread.wait(t)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to wait on thread: \", res)\n            return\n        end\n\n        ngx.log(ngx.INFO, \"uthread: \", res)\n\n        local ok, err = ngx.thread.kill(t)\n        if not ok then\n            ngx.log(ngx.INFO, \"uthread: failed to kill: \", err)\n        end\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;\n        ssl_session_tickets off;\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;\n    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(5000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(package.loaded.session, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                package.loaded.session = sess\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nclose: 1 nil\n--- grep_error_log eval: qr/uthread: [^.,]+/\n--- grep_error_log_out eval\n[\n'',\n'uthread: hello from f()\nuthread: 32\nuthread: failed to kill: already waited or killed\n',\n'uthread: hello from f()\nuthread: 32\nuthread: failed to kill: already waited or killed\n'\n]\n--- no_error_log\n[alert]\n[error]\n[emerg]\n--- skip_openssl: 6: > 1.1.1w\n"
  },
  {
    "path": "t/144-shdict-incr-init.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse lib 'lib';\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 0);\n\n#no_diff();\nno_long_string();\n#master_on();\n#workers(2);\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: incr key with init (key exists)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 32)\n            local res, err = dogs:incr(\"foo\", 10502, 1)\n            ngx.say(\"incr: \", res, \" \", err)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        }\n    }\n--- request\nGET /test\n--- response_body\nincr: 10534 nil\nfoo = 10534\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: incr key with init (key not exists)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n            dogs:flush_all()\n            dogs:set(\"bah\", 32)\n            local res, err = dogs:incr(\"foo\", 10502, 1)\n            ngx.say(\"incr: \", res, \" \", err)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        }\n    }\n--- request\nGET /test\n--- response_body\nincr: 10503 nil\nfoo = 10503\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: incr key with init (key expired and size not matched)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n            for i = 1, 20 do\n                dogs:set(\"bar\" .. i, i, 0.001)\n            end\n            dogs:set(\"foo\", \"32\", 0.001)\n            ngx.location.capture(\"/sleep/0.002\")\n            local res, err = dogs:incr(\"foo\", 10502, 0)\n            ngx.say(\"incr: \", res, \" \", err)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        }\n    }\n    location ~ ^/sleep/(.+) {\n        echo_sleep $1;\n    }\n--- request\nGET /test\n--- response_body\nincr: 10502 nil\nfoo = 10502\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: incr key with init (key expired and size matched)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n            for i = 1, 20 do\n                dogs:set(\"bar\" .. i, i, 0.001)\n            end\n            dogs:set(\"foo\", 32, 0.001)\n            ngx.location.capture(\"/sleep/0.002\")\n            local res, err = dogs:incr(\"foo\", 10502, 0)\n            ngx.say(\"incr: \", res, \" \", err)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        }\n    }\n    location ~ ^/sleep/(.+) {\n        echo_sleep $1;\n    }\n--- request\nGET /test\n--- response_body\nincr: 10502 nil\nfoo = 10502\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: incr key with init (forcibly override other valid entries)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n            dogs:flush_all()\n            local long_prefix = string.rep(\"1234567890\", 100)\n            for i = 1, 1000 do\n                local success, err, forcible = dogs:set(long_prefix .. i, i)\n                if forcible then\n                    dogs:delete(long_prefix .. i)\n                    break\n                end\n            end\n            local res, err, forcible = dogs:incr(long_prefix .. \"bar\", 10502, 0)\n            ngx.say(\"incr: \", res, \" \", err, \" \", forcible)\n            local res, err, forcible = dogs:incr(long_prefix .. \"foo\", 10502, 0)\n            ngx.say(\"incr: \", res, \" \", err, \" \", forcible)\n            ngx.say(\"foo = \", dogs:get(long_prefix .. \"foo\"))\n        }\n    }\n--- request\nGET /test\n--- response_body\nincr: 10502 nil false\nincr: 10502 nil true\nfoo = 10502\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: incr key without init (no forcible returned)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", 1)\n            local res, err, forcible = dogs:incr(\"foo\", 1)\n            ngx.say(\"incr: \", res, \" \", err, \" \", forcible)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        }\n    }\n--- request\nGET /test\n--- response_body\nincr: 2 nil nil\nfoo = 2\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: incr key (original value is not number)\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n            dogs:set(\"foo\", true)\n            local res, err = dogs:incr(\"foo\", 1, 0)\n            ngx.say(\"incr: \", res, \" \", err)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        }\n    }\n--- request\nGET /test\n--- response_body\nincr: nil not a number\nfoo = true\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: init is not number\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n            local res, err, forcible = dogs:incr(\"foo\", 1, \"bar\")\n            ngx.say(\"incr: \", res, \" \", err, \" \", forcible)\n            ngx.say(\"foo = \", dogs:get(\"foo\"))\n        }\n    }\n--- request\nGET /test\n--- error_code: 500\n--- response_body_like: 500 Internal Server Error\n--- error_log\nnumber expected, got string\n"
  },
  {
    "path": "t/145-shdict-list.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\n#repeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 0);\n\n#no_diff();\nno_long_string();\n#master_on();\n#workers(2);\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: lpush & lpop\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local len, err = dogs:lpush(\"foo\", \"bar\")\n            if len then\n                ngx.say(\"push success\")\n            else\n                ngx.say(\"push err: \", err)\n            end\n\n            local val, err = dogs:llen(\"foo\")\n            ngx.say(val, \" \", err)\n\n            local val, err = dogs:lpop(\"foo\")\n            ngx.say(val, \" \", err)\n\n            local val, err = dogs:llen(\"foo\")\n            ngx.say(val, \" \", err)\n\n            local val, err = dogs:lpop(\"foo\")\n            ngx.say(val, \" \", err)\n        }\n    }\n--- request\nGET /test\n--- response_body\npush success\n1 nil\nbar nil\n0 nil\nnil nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: get operation on list type\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local len, err = dogs:lpush(\"foo\", \"bar\")\n            if len then\n                ngx.say(\"push success\")\n            else\n                ngx.say(\"push err: \", err)\n            end\n\n            local val, err = dogs:get(\"foo\")\n            ngx.say(val, \" \", err)\n        }\n    }\n--- request\nGET /test\n--- response_body\npush success\nnil value is a list\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: set operation on list type\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local len, err = dogs:lpush(\"foo\", \"bar\")\n            if len then\n                ngx.say(\"push success\")\n            else\n                ngx.say(\"push err: \", err)\n            end\n\n            local ok, err = dogs:set(\"foo\", \"bar\")\n            ngx.say(ok, \" \", err)\n\n            local val, err = dogs:get(\"foo\")\n            ngx.say(val, \" \", err)\n        }\n    }\n--- request\nGET /test\n--- response_body\npush success\ntrue nil\nbar nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: replace operation on list type\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local len, err = dogs:lpush(\"foo\", \"bar\")\n            if len then\n                ngx.say(\"push success\")\n            else\n                ngx.say(\"push err: \", err)\n            end\n\n            local ok, err = dogs:replace(\"foo\", \"bar\")\n            ngx.say(ok, \" \", err)\n\n            local val, err = dogs:get(\"foo\")\n            ngx.say(val, \" \", err)\n        }\n    }\n--- request\nGET /test\n--- response_body\npush success\ntrue nil\nbar nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: add operation on list type\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local len, err = dogs:lpush(\"foo\", \"bar\")\n            if len then\n                ngx.say(\"push success\")\n            else\n                ngx.say(\"push err: \", err)\n            end\n\n            local ok, err = dogs:add(\"foo\", \"bar\")\n            ngx.say(ok, \" \", err)\n\n            local val, err = dogs:get(\"foo\")\n            ngx.say(val, \" \", err)\n        }\n    }\n--- request\nGET /test\n--- response_body\npush success\nfalse exists\nnil value is a list\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: delete operation on list type\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local len, err = dogs:lpush(\"foo\", \"bar\")\n            if len then\n                ngx.say(\"push success\")\n            else\n                ngx.say(\"push err: \", err)\n            end\n\n            local ok, err = dogs:delete(\"foo\")\n            ngx.say(ok, \" \", err)\n\n            local val, err = dogs:get(\"foo\")\n            ngx.say(val, \" \", err)\n        }\n    }\n--- request\nGET /test\n--- response_body\npush success\ntrue nil\nnil nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: incr operation on list type\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local len, err = dogs:lpush(\"foo\", \"bar\")\n            if len then\n                ngx.say(\"push success\")\n            else\n                ngx.say(\"push err: \", err)\n            end\n\n            local ok, err = dogs:incr(\"foo\", 1)\n            ngx.say(ok, \" \", err)\n\n            local val, err = dogs:get(\"foo\")\n            ngx.say(val, \" \", err)\n        }\n    }\n--- request\nGET /test\n--- response_body\npush success\nnil not a number\nnil value is a list\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: get_keys operation on list type\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local len, err = dogs:lpush(\"foo\", \"bar\")\n            if len then\n                ngx.say(\"push success\")\n            else\n                ngx.say(\"push err: \", err)\n            end\n\n            local keys, err = dogs:get_keys()\n            ngx.say(\"key: \", keys[1])\n        }\n    }\n--- request\nGET /test\n--- response_body\npush success\nkey: foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: push operation on key-value type\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local ok, err = dogs:set(\"foo\", \"bar\")\n            if ok then\n                ngx.say(\"set success\")\n            else\n                ngx.say(\"set err: \", err)\n            end\n\n            local len, err = dogs:lpush(\"foo\", \"bar\")\n            ngx.say(len, \" \", err)\n\n            local val, err = dogs:get(\"foo\")\n            ngx.say(val, \" \", err)\n        }\n    }\n--- request\nGET /test\n--- response_body\nset success\nnil value not a list\nbar nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: pop operation on key-value type\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local ok, err = dogs:set(\"foo\", \"bar\")\n            if ok then\n                ngx.say(\"set success\")\n            else\n                ngx.say(\"set err: \", err)\n            end\n\n            local val, err = dogs:lpop(\"foo\")\n            ngx.say(val, \" \", err)\n\n            local val, err = dogs:get(\"foo\")\n            ngx.say(val, \" \", err)\n        }\n    }\n--- request\nGET /test\n--- response_body\nset success\nnil value not a list\nbar nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: llen operation on key-value type\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local ok, err = dogs:set(\"foo\", \"bar\")\n            if ok then\n                ngx.say(\"set success\")\n            else\n                ngx.say(\"set err: \", err)\n            end\n\n            local val, err = dogs:llen(\"foo\")\n            ngx.say(val, \" \", err)\n\n            local val, err = dogs:get(\"foo\")\n            ngx.say(val, \" \", err)\n        }\n    }\n--- request\nGET /test\n--- response_body\nset success\nnil value not a list\nbar nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: lpush and lpop\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            for i = 1, 3 do\n                local len, err = dogs:lpush(\"foo\", i)\n                if len ~= i then\n                    ngx.say(\"push err: \", err)\n                    break\n                end\n            end\n\n            for i = 1, 3 do\n                local val, err = dogs:lpop(\"foo\")\n                if not val then\n                    ngx.say(\"pop err: \", err)\n                    break\n                else\n                    ngx.say(val)\n                end\n            end\n        }\n    }\n--- request\nGET /test\n--- response_body\n3\n2\n1\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: lpush and rpop\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            for i = 1, 3 do\n                local len, err = dogs:lpush(\"foo\", i)\n                if len ~= i then\n                    ngx.say(\"push err: \", err)\n                    break\n                end\n            end\n\n            for i = 1, 3 do\n                local val, err = dogs:rpop(\"foo\")\n                if not val then\n                    ngx.say(\"pop err: \", err)\n                    break\n                else\n                    ngx.say(val)\n                end\n            end\n        }\n    }\n--- request\nGET /test\n--- response_body\n1\n2\n3\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: rpush and lpop\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            for i = 1, 3 do\n                local len, err = dogs:rpush(\"foo\", i)\n                if len ~= i then\n                    ngx.say(\"push err: \", err)\n                    break\n                end\n            end\n\n            for i = 1, 3 do\n                local val, err = dogs:lpop(\"foo\")\n                if not val then\n                    ngx.say(\"pop err: \", err)\n                    break\n                else\n                    ngx.say(val)\n                end\n            end\n        }\n    }\n--- request\nGET /test\n--- response_body\n1\n2\n3\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: list removed: expired\n--- http_config\n    lua_shared_dict dogs 900k;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local N = 100000\n            local max = 0\n\n            for i = 1, N do\n                local key = string.format(\"%05d\", i)\n\n                local len , err = dogs:lpush(key, i)\n                if not len then\n                    max = i\n                    break\n                end\n            end\n\n            local keys = dogs:get_keys(0)\n\n            ngx.say(\"max - 1 matched keys length: \", max - 1 == #keys)\n\n            dogs:flush_all()\n\n            local keys = dogs:get_keys(0)\n\n            ngx.say(\"keys all expired, left number: \", #keys)\n\n            for i = 100000, 1, -1 do\n                local key = string.format(\"%05d\", i)\n\n                local len, err = dogs:lpush(key, i)\n                if not len then\n                    ngx.say(\"loop again, max matched: \", N + 1 - i == max)\n                    break\n                end\n            end\n\n            dogs:flush_all()\n\n            dogs:flush_expired()\n\n            for i = 1, N do\n                local key = string.format(\"%05d\", i)\n\n                local len, err = dogs:lpush(key, i)\n                if not len then\n                    ngx.say(\"loop again, max matched: \", i == max)\n                    break\n                end\n            end\n        }\n    }\n--- request\nGET /test\n--- response_body\nmax - 1 matched keys length: true\nkeys all expired, left number: 0\nloop again, max matched: true\nloop again, max matched: true\n--- no_error_log\n[error]\n--- timeout: 9\n\n\n\n=== TEST 16: list removed: forcibly\n--- http_config\n    lua_shared_dict dogs 900k;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local N = 200000\n            local max = 0\n            for i = 1, N do\n                local ok, err, forcible  = dogs:set(i, i)\n                if not ok or forcible then\n                    max = i\n                    break\n                end\n            end\n\n            local two = dogs:get(2)\n\n            ngx.say(\"two == number 2: \", two == 2)\n\n            dogs:flush_all()\n            dogs:flush_expired()\n\n            local keys = dogs:get_keys(0)\n\n            ngx.say(\"no one left: \", #keys)\n\n            for i = 1, N do\n                local key = string.format(\"%05d\", i)\n\n                local len, err = dogs:lpush(key, i)\n                if not len then\n                    break\n                end\n            end\n\n            for i = 1, max do\n                local ok, err = dogs:set(i, i)\n                if not ok then\n                    ngx.say(\"set err: \", err)\n                    break\n                end\n            end\n\n            local two = dogs:get(2)\n\n            ngx.say(\"two == number 2: \", two == 2)\n        }\n    }\n--- request\nGET /test\n--- response_body\ntwo == number 2: true\nno one left: 0\ntwo == number 2: true\n--- no_error_log\n[error]\n--- timeout: 9\n\n\n\n=== TEST 17: expire on all types\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local len, err = dogs:lpush(\"list\", \"foo\")\n            if not len then\n                ngx.say(\"push err: \", err)\n            end\n\n            local ok, err = dogs:set(\"key\", \"bar\")\n            if not ok then\n                ngx.say(\"set err: \", err)\n            end\n\n            local keys = dogs:get_keys(0)\n\n            ngx.say(\"keys number: \", #keys)\n\n            dogs:flush_all()\n\n            local keys = dogs:get_keys(0)\n\n            ngx.say(\"keys number: \", #keys)\n        }\n    }\n--- request\nGET /test\n--- response_body\nkeys number: 2\nkeys number: 0\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: long list node\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local long_str = string.rep(\"foo\", 10)\n\n            for i = 1, 3 do\n                local len, err = dogs:lpush(\"list\", long_str)\n                if not len then\n                    ngx.say(\"push err: \", err)\n                end\n            end\n\n            for i = 1, 3 do\n                local val, err = dogs:lpop(\"list\")\n                if val then\n                    ngx.say(val)\n                end\n            end\n        }\n    }\n--- request\nGET /test\n--- response_body\nfoofoofoofoofoofoofoofoofoofoo\nfoofoofoofoofoofoofoofoofoofoo\nfoofoofoofoofoofoofoofoofoofoo\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: incr on expired list\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n\n            local long_str = string.rep(\"foo\", 10 * 1024) -- 30k\n\n            for i = 1, 100 do\n                for j = 1, 10 do\n                    local key = \"list\" .. j\n                    local len, err = dogs:lpush(key, long_str)\n                    if not len then\n                        ngx.say(\"push err: \", err)\n                    end\n                end\n\n                dogs:flush_all()\n\n                for j = 10, 1, -1 do\n                    local key = \"list\" .. j\n                    local newval, err = dogs:incr(key, 1, 0)\n                    if not newval then\n                        ngx.say(\"incr err: \", err)\n                    end\n                end\n\n                dogs:flush_all()\n            end\n\n            ngx.say(\"done\")\n        }\n    }\n--- request\nGET /test\n--- response_body\ndone\n--- no_error_log\n[error]\n\n\n\n=== TEST 20: push to an expired list\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n            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))\n            if not len then\n                ngx.say(\"push cc  err: \", err)\n            end\n            local len, err = dogs:lpush(\"aa\", \"1\")\n            if not len then\n                ngx.say(\"push1 err: \", err)\n            end\n            local succ, err = dogs:expire(\"aa\", 0.2)\n            if not succ then\n                ngx.say(\"expire err: \",err)\n            end\n            ngx.sleep(0.3) -- list aa expired\n            local len, err = dogs:lpush(\"aa\", \"2\") --push to an expired list may set as a new list\n            if not len then\n                ngx.say(\"push2 err: \", err)\n            end\n            local len, err = dogs:llen(\"aa\") -- new list len is 1\n            if not len then\n                ngx.say(\"llen err: \", err)\n            else\n            ngx.say(\"aa:len :\", dogs:llen(\"aa\"))\n            end\n        }\n    }\n\n--- request\nGET /test\n--- response_body\naa:len :1\n--- no_error_log\n[error]\n\n\n\n=== TEST 21: push to an expired list then pop many time (more then list len )\n--- http_config\n    lua_shared_dict dogs 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n            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))\n            if not len then\n                ngx.say(\"push cc  err: \", err)\n            end\n            local len, err = dogs:lpush(\"aa\", \"1\")\n            if not len then\n                ngx.say(\"push1 err: \", err)\n            end\n            local succ, err = dogs:expire(\"aa\", 0.2)\n            if not succ then\n            ngx.say(\"expire err: \",err)\n            end\n            ngx.sleep(0.3) -- list aa expired\n            local len, err = dogs:lpush(\"aa\", \"2\") --push to an expired list may set as a new list\n            if not len then\n                ngx.say(\"push2 err: \", err)\n            end\n            local val, err = dogs:lpop(\"aa\") \n            if not val then\n                ngx.say(\"llen err: \", err)\n            end\n            local val, err = dogs:lpop(\"aa\")  -- val == nil\n            ngx.say(\"aa list value: \", val)\n        }\n    }\n\n--- request\nGET /test\n--- response_body\naa list value: nil\n--- no_error_log\n[error]\n\n\n\n=== TEST 22: lpush return nil\n--- http_config\n    lua_shared_dict dogs 100k;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n            for i = 1, 2920\n            do\n                local len, err = dogs:lpush(\"foo\", \"bar\")\n            end\n            local len, err = dogs:lpush(\"foo\", \"bar\")\n            ngx.say(len)\n        }\n    }\n--- request\nGET /test\n--- response_body\nnil\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/146-malloc-trim.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\n#repeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4 + 3);\n\n#no_diff();\nno_long_string();\n#master_on();\n#workers(2);\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: malloc_trim() every 1 req, in subreq\n--- http_config\n    lua_malloc_trim 1;\n--- config\n    location = /t {\n        return 200 \"ok\\n\";\n    }\n\n    location = /main {\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n    }\n--- request\nGET /main\n--- response_body\nok\nok\nok\nok\nok\n--- grep_error_log eval: qr/malloc_trim\\(\\d+\\) returned \\d+/\n--- grep_error_log_out eval\nqr/\\Amalloc_trim\\(1\\) returned [01]\n\\z/\n--- wait: 0.2\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: malloc_trim() every 1 req, in subreq\n--- http_config\n    lua_malloc_trim 1;\n--- config\n    location = /t {\n        log_subrequest on;\n        return 200 \"ok\\n\";\n    }\n\n    location = /main {\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n    }\n--- request\nGET /main\n--- response_body\nok\nok\nok\nok\nok\n--- grep_error_log eval: qr/malloc_trim\\(\\d+\\) returned \\d+/\n--- grep_error_log_out eval\nqr/\\Amalloc_trim\\(1\\) returned [01]\nmalloc_trim\\(1\\) returned [01]\nmalloc_trim\\(1\\) returned [01]\nmalloc_trim\\(1\\) returned [01]\nmalloc_trim\\(1\\) returned [01]\nmalloc_trim\\(1\\) returned [01]\n\\z/\n--- wait: 0.2\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: malloc_trim() every 2 req, in subreq\n--- http_config\n    lua_malloc_trim 2;\n--- config\n    location = /t {\n        log_subrequest on;\n        return 200 \"ok\\n\";\n    }\n\n    location = /main {\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n    }\n--- request\nGET /main\n--- response_body\nok\nok\nok\nok\nok\n--- grep_error_log eval: qr/malloc_trim\\(\\d+\\) returned \\d+/\n--- grep_error_log_out eval\nqr/\\Amalloc_trim\\(1\\) returned [01]\nmalloc_trim\\(1\\) returned [01]\nmalloc_trim\\(1\\) returned [01]\n\\z/\n--- wait: 0.2\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: malloc_trim() every 3 req, in subreq\n--- http_config\n    lua_malloc_trim 3;\n--- config\n    location = /t {\n        log_subrequest on;\n        return 200 \"ok\\n\";\n    }\n\n    location = /main {\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n    }\n--- request\nGET /main\n--- response_body\nok\nok\nok\nok\nok\n--- grep_error_log eval: qr/malloc_trim\\(\\d+\\) returned \\d+/\n--- grep_error_log_out eval\nqr/\\Amalloc_trim\\(1\\) returned [01]\nmalloc_trim\\(1\\) returned [01]\n\\z/\n--- wait: 0.2\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: malloc_trim() every 2 req, in subreq, big memory usage\n--- http_config\n    lua_malloc_trim 2;\n    lua_package_path \"$prefix/html/?.lua;;\";\n--- config\n    location = /t {\n        log_subrequest on;\n        content_by_lua_block {\n            require(\"foo\")()\n        }\n    }\n\n    location = /main {\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n    }\n--- user_files\n>>> foo.lua\nlocal ffi = require \"ffi\"\n\nffi.cdef[[\n    void *malloc(size_t sz);\n    void free(void *p);\n]]\n\nreturn function ()\n    local t = {}\n    for i = 1, 10 do\n        t[i] = ffi.C.malloc(1024 * 128)\n    end\n    for i = 1, 10 do\n        ffi.C.free(t[i])\n    end\n    ngx.say(\"ok\")\nend\n--- request\nGET /main\n--- response_body\nok\nok\nok\nok\nok\n--- grep_error_log eval: qr/malloc_trim\\(\\d+\\) returned \\d+/\n--- grep_error_log_out\nmalloc_trim(1) returned 1\nmalloc_trim(1) returned 1\nmalloc_trim(1) returned 1\n--- wait: 0.2\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: zero count means off\n--- http_config\n    lua_malloc_trim 0;\n    lua_package_path \"$prefix/html/?.lua;;\";\n--- config\n    location = /t {\n        content_by_lua_block {\n            require(\"foo\")()\n        }\n    }\n--- user_files\n>>> foo.lua\nlocal ffi = require \"ffi\"\n\nffi.cdef[[\n    void *malloc(size_t sz);\n    void free(void *p);\n]]\n\nreturn function ()\n    local t = {}\n    for i = 1, 10 do\n        t[i] = ffi.C.malloc(1024 * 128)\n    end\n    for i = 1, 10 do\n        ffi.C.free(t[i])\n    end\n    ngx.say(\"ok\")\nend\n\n--- request\nGET /t\n--- response_body\nok\n--- grep_error_log eval: qr/malloc_trim\\(\\d+\\) returned \\d+/\n--- grep_error_log_out\n--- wait: 0.2\n--- no_error_log\nmalloc_trim() disabled\n[error]\n\n\n\n=== TEST 7: zero count means off, log_by_lua\n--- http_config\n    lua_malloc_trim 0;\n    lua_package_path \"$prefix/html/?.lua;;\";\n--- config\n    location = /t {\n        content_by_lua_block {\n            require(\"foo\")()\n        }\n        log_by_lua_block {\n            print(\"Hello from log\")\n        }\n    }\n--- user_files\n>>> foo.lua\nlocal ffi = require \"ffi\"\n\nffi.cdef[[\n    void *malloc(size_t sz);\n    void free(void *p);\n]]\n\nreturn function ()\n    local t = {}\n    for i = 1, 10 do\n        t[i] = ffi.C.malloc(1024 * 128)\n    end\n    for i = 1, 10 do\n        ffi.C.free(t[i])\n    end\n    ngx.say(\"ok\")\nend\n\n--- request\nGET /t\n--- response_body\nok\n--- grep_error_log eval: qr/malloc_trim\\(\\d+\\) returned \\d+/\n--- grep_error_log_out\n--- wait: 0.2\n--- error_log\nHello from log\nmalloc_trim() disabled\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: malloc_trim() every 1 req\n--- http_config\n    lua_malloc_trim 1;\n--- config\n    location = /t {\n        return 200 \"ok\\n\";\n    }\n\n    location = /main {\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n        echo_location /t;\n    }\n--- request\nGET /main\n--- response_body\nok\nok\nok\nok\nok\n--- grep_error_log eval: qr/malloc_trim\\(\\d+\\) returned \\d+/\n--- grep_error_log_out eval\nqr/\\Amalloc_trim\\(1\\) returned [01]\n\\z/\n--- wait: 0.2\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/147-tcp-socket-timeouts.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nBEGIN {\n    if (!defined $ENV{LD_PRELOAD}) {\n        $ENV{LD_PRELOAD} = '';\n    }\n\n    if ($ENV{LD_PRELOAD} !~ /\\bmockeagain\\.so\\b/) {\n        $ENV{LD_PRELOAD} = \"mockeagain.so $ENV{LD_PRELOAD}\";\n    }\n\n    if ($ENV{MOCKEAGAIN} eq 'r') {\n        $ENV{MOCKEAGAIN} = 'rw';\n\n    } else {\n        $ENV{MOCKEAGAIN} = 'w';\n    }\n\n    $ENV{TEST_NGINX_EVENT_TYPE} = 'poll';\n    delete($ENV{TEST_NGINX_USE_HTTP2});\n    $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'slowdata';\n}\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 1);\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- config\n    server_tokens off;\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            sock:settimeouts(150, 150, 150)  -- 150ms read timeout\n\n            local port = ngx.var.server_port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n\n    location /foo {\n        content_by_lua_block {\n            ngx.sleep(0.01) -- 10 ms\n            ngx.say(\"foo\")\n        }\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body_like\nreceived: foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: read timeout\n--- config\n    server_tokens off;\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            sock:settimeouts(150, 150, 2)  -- 2ms read timeout\n\n            local port = ngx.var.server_port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n\n    location /foo {\n        content_by_lua_block {\n            ngx.sleep(0.01) -- 10 ms\n            ngx.say(\"foo\")\n        }\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body_like\nfailed to receive a line: timeout \\[\\]\n--- error_log\nlua tcp socket read timed out\n\n\n\n=== TEST 3: send ok\n--- config\n    server_tokens off;\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            sock:settimeouts(500, 500, 500)  -- 500ms timeout\n\n            local port = ngx.var.server_port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local data = string.rep(\"a\", 8) -- 8 bytes\n            local num = 10 -- total: 80 bytes\n\n            local req = \"POST /foo HTTP/1.0\\r\\nHost: localhost\\r\\nContent-Length: \"\n                        .. #data * num .. \"\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            for i = 1, num do\n                local bytes, err = sock:send(data)\n                if not bytes then\n                    ngx.say(\"failed to send body: \", err)\n                    return\n                end\n            end\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n\n    location /foo {\n        content_by_lua_block {\n            local content_length = ngx.req.get_headers()[\"Content-Length\"]\n\n            local sock = ngx.req.socket()\n\n            sock:settimeouts(500, 500, 500)\n\n            local chunk = 8\n\n            for i = 1, content_length, chunk do\n                local data, err = sock:receive(chunk)\n                if not data then\n                    ngx.say(\"failed to receive chunk: \", err)\n                    return\n                end\n            end\n\n            ngx.say(\"got len: \", content_length)\n        }\n    }\n\n--- request\nGET /t\n--- response_body_like\nreceived: got len: 80\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: send timeout\n--- config\n    server_tokens off;\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            sock:settimeouts(500, 500, 500)  -- 500ms timeout\n\n            local port = ngx.var.server_port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local data = \"slowdata\" -- slow data\n            local num = 10\n\n            local req = \"POST /foo HTTP/1.0\\r\\nHost: localhost\\r\\nContent-Length: \"\n                        .. #data * num .. \"\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            for i = 1, num do\n                local bytes, err = sock:send(data)\n                if not bytes then\n                    ngx.say(\"failed to send body: \", err)\n                    return\n                end\n            end\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n\n    location /foo {\n        content_by_lua_block {\n            local content_length = ngx.req.get_headers()[\"Content-Length\"]\n\n            local sock = ngx.req.socket()\n\n            sock:settimeouts(500, 500, 500)\n\n            local chunk = 8\n\n            for i = 1, content_length, chunk do\n                local data, err = sock:receive(chunk)\n                if not data then\n                    ngx.say(\"failed to receive chunk: \", err)\n                    return\n                end\n            end\n\n            ngx.say(\"got len: \", content_length)\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nfailed to send body: timeout\n--- error_log\nlua tcp socket write timed out\n\n\n\n=== TEST 5: connection timeout (tcp)\n--- config\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    resolver_timeout 3s;\n    location /test {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            sock:settimeouts(100, 100, 100)\n\n            local ok, err = sock:connect(\"127.0.0.2\", 12345)\n            ngx.say(\"connect: \", ok, \" \", err)\n\n            local bytes\n            bytes, err = sock:send(\"hello\")\n            ngx.say(\"send: \", bytes, \" \", err)\n\n            local line\n            line, err = sock:receive()\n            ngx.say(\"receive: \", line, \" \", err)\n\n            ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n--- request\n    GET /test\n--- response_body\nconnect: nil timeout\nsend: nil closed\nreceive: nil closed\nclose: nil closed\n--- error_log\nlua tcp socket connect timed out, upstream: 127.0.0.2:12345(127.0.0.2)\n--- timeout: 10\n\n\n\n=== TEST 6: different timeout with duplex socket (settimeout)\n--- config\n    server_tokens off;\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            sock:settimeouts(200, 200, 200)  -- 200ms timeout\n\n            local port = ngx.var.server_port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local data = string.rep(\"a\", 4) -- 4 bytes\n            local num = 3\n\n            local req = \"POST /foo HTTP/1.0\\r\\nHost: localhost\\r\\nContent-Length: \"\n                        .. #data * num .. \"\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            for i = 1, num do\n                local bytes, err = sock:send(data)\n                if not bytes then\n                    ngx.log(ngx.ERR, \"failed to send body: \", err)\n                    return\n                end\n                ngx.sleep(0.12) -- 120ms\n            end\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n\n    location /foo {\n        content_by_lua_block {\n            local content_length = ngx.req.get_headers()[\"Content-Length\"]\n\n            local sock = ngx.req.socket(true)\n\n            local chunk = 4\n\n            local function read()\n                sock:settimeout(200) -- read: 200 ms\n\n                local data, err = sock:receive(content_length)\n                if not data then\n                    ngx.log(ngx.ERR, \"failed to receive data: \", err)\n                    return\n                end\n            end\n\n            local co = ngx.thread.spawn(read)\n\n            sock:settimeout(100) -- send: 100ms\n            sock:send(\"ok\\n\")\n\n            local ok, err = ngx.thread.wait(co)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to wait co: \", err)\n            end\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nreceived: ok\nfailed to receive a line: closed []\n--- error_log\nlua tcp socket read timed out\nfailed to receive data: timeout\n\n\n\n=== TEST 7: different timeout with duplex socket (settimeouts)\n--- config\n    server_tokens off;\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            sock:settimeouts(200, 200, 200)  -- 200ms timeout\n\n            local port = ngx.var.server_port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local data = string.rep(\"a\", 4) -- 4 bytes\n            local num = 3\n\n            local req = \"POST /foo HTTP/1.0\\r\\nHost: localhost\\r\\nContent-Length: \"\n                        .. #data * num .. \"\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            for i = 1, num do\n                local bytes, err = sock:send(data)\n                if not bytes then\n                    ngx.log(ngx.ERR, \"failed to send body: \", err)\n                    return\n                end\n                ngx.sleep(0.12) -- 120ms\n            end\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n\n    location /foo {\n        content_by_lua_block {\n            local content_length = ngx.req.get_headers()[\"Content-Length\"]\n\n            local sock = ngx.req.socket(true)\n\n            sock:settimeouts(0, 100, 200) -- send: 100ms, read: 200ms\n\n            local chunk = 4\n\n            local function read()\n                local data, err = sock:receive(content_length)\n                if not data then\n                    ngx.log(ngx.ERR, \"failed to receive data: \", err)\n                    return\n                end\n            end\n\n            local co = ngx.thread.spawn(read)\n\n            sock:send(\"ok\\n\")\n\n            local ok, err = ngx.thread.wait(co)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to wait co: \", err)\n            end\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nreceived: ok\nfailed to receive a line: closed []\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: connection timeout overflow detection\n--- config\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local ok, err = pcall(sock.settimeouts, sock,\n                                  (2 ^ 31) - 1, 500, 500)\n            if not ok then\n                ngx.say(\"failed to set timeouts: \", err)\n            else\n                ngx.say(\"settimeouts: ok\")\n            end\n\n            ok, err = pcall(sock.settimeouts, sock, 2 ^ 31, 500, 500)\n            if not ok then\n                ngx.say(\"failed to set timeouts: \", err)\n            else\n                ngx.say(\"settimeouts: ok\")\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body_like\nsettimeouts: ok\nfailed to set timeouts: bad timeout value\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: send timeout overflow detection\n--- config\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local ok, err = pcall(sock.settimeouts, sock,\n                                  500, (2 ^ 31) - 1, 500)\n            if not ok then\n                ngx.say(\"failed to set timeouts: \", err)\n            else\n                ngx.say(\"settimeouts: ok\")\n            end\n\n            ok, err = pcall(sock.settimeouts, sock, 500, 2 ^ 31, 500)\n            if not ok then\n                ngx.say(\"failed to set timeouts: \", err)\n            else\n                ngx.say(\"settimeouts: ok\")\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body_like\nsettimeouts: ok\nfailed to set timeouts: bad timeout value\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: read timeout overflow detection\n--- config\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local ok, err = pcall(sock.settimeouts, sock,\n                                  500, 500, (2 ^ 31) - 1)\n            if not ok then\n                ngx.say(\"failed to set timeouts: \", err)\n            else\n                ngx.say(\"settimeouts: ok\")\n            end\n\n            ok, err = pcall(sock.settimeouts, sock, 500, 500, 2 ^ 31)\n            if not ok then\n                ngx.say(\"failed to set timeouts: \", err)\n            else\n                ngx.say(\"settimeouts: ok\")\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body_like\nsettimeouts: ok\nfailed to set timeouts: bad timeout value\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/148-fake-shm-zone.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse lib 'lib';\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\n#repeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 - 1);\n\n#no_diff();\nno_long_string();\n#master_on();\n#workers(2);\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: require test\n--- http_config\n    lua_fake_shm x1 1m;\n    lua_fake_shm x2 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local shm_zones = require(\"fake_shm_zones\")\n            ngx.say(type(shm_zones))\n            local x1 = shm_zones.x1\n            ngx.say(type(x1))\n            local x2 = shm_zones.x2\n            ngx.say(type(x1))\n        }\n    }\n--- request\nGET /test\n--- response_body\ntable\ntable\ntable\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: index shm_zone\n--- http_config\n    lua_fake_shm x1 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local shm_zones = require(\"fake_shm_zones\")\n            local x1 = shm_zones.x1\n            ngx.say(type(x1))\n        }\n    }\n--- request\nGET /test\n--- response_body\ntable\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: get_info\n--- http_config\n    lua_fake_shm x1 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local shm_zones = require(\"fake_shm_zones\")\n            local name, size, isinit, isold\n            local x1 = shm_zones.x1\n\n            name, size, isinit, isold = x1:get_info()\n            ngx.say(\"name=\", name)\n            ngx.say(\"size=\", size)\n            ngx.say(\"isinit=\", isinit)\n            ngx.say(\"isold=\", isold)\n        }\n    }\n--- request\nGET /test\n--- response_body\nname=x1\nsize=1048576\nisinit=true\nisold=false\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: multiply zones\n--- http_config\n    lua_fake_shm x1 1m;\n    lua_fake_shm x2 2m;\n    lua_fake_shm x3 3m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local shm_zones = require(\"fake_shm_zones\")\n            local name, size, isinit, isold\n            local x1 = shm_zones.x1\n            local x2 = shm_zones.x2\n            local x3 = shm_zones.x3\n\n            name, size, isinit, isold = x1:get_info()\n            ngx.say(\"name=\", name)\n            ngx.say(\"size=\", size)\n            ngx.say(\"isinit=\", isinit)\n            ngx.say(\"isold=\", isold)\n\n            name, size, isinit, isold = x2:get_info()\n            ngx.say(\"name=\", name)\n            ngx.say(\"size=\", size)\n            ngx.say(\"isinit=\", isinit)\n            ngx.say(\"isold=\", isold)\n\n            name, size, isinit, isold = x3:get_info()\n            ngx.say(\"name=\", name)\n            ngx.say(\"size=\", size)\n            ngx.say(\"isinit=\", isinit)\n            ngx.say(\"isold=\", isold)\n        }\n    }\n--- request\nGET /test\n--- response_body\nname=x1\nsize=1048576\nisinit=true\nisold=false\nname=x2\nsize=2097152\nisinit=true\nisold=false\nname=x3\nsize=3145728\nisinit=true\nisold=false\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: duplicate zones\n--- http_config\n    lua_fake_shm x1 1m;\n    lua_fake_shm x1 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local shm_zones = require(\"fake_shm_zones\")\n            local x1 = shm_zones.x1\n            ngx.say(\"error\")\n        }\n    }\n--- request\nGET /test\n--- request_body_unlike\nerror\n--- must_die\n--- error_log\nlua_fake_shm \"x1\" is already defined as \"x1\"\n--- error_log\n[emerg]\n"
  },
  {
    "path": "t/149-hup-fake-shm-zone.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_CHECK_LEAK}) {\n        $SkipReason = \"unavailable for the hup tests\";\n\n    } else {\n        $ENV{TEST_NGINX_USE_HUP} = 1;\n        undef $ENV{TEST_NGINX_USE_STAP};\n    }\n}\n\nuse lib 'lib';\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\nno_long_string();\n#master_on();\n#workers(2);\n\nno_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: get_info, before HUP reload\n--- http_config\n    lua_fake_shm x1 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local shm_zones = require(\"fake_shm_zones\")\n            local name, size, isinit, isold\n            local x1 = shm_zones.x1\n\n            name, size, isinit, isold = x1:get_info()\n            ngx.say(\"name=\", name)\n            ngx.say(\"size=\", size)\n            ngx.say(\"isinit=\", isinit)\n            ngx.say(\"isold=\", isold)\n        }\n    }\n--- request\nGET /test\n--- response_body\nname=x1\nsize=1048576\nisinit=true\nisold=false\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: get_info, after HUP reload\n--- http_config\n    lua_fake_shm x1 1m;\n--- config\n    location = /test {\n        content_by_lua_block {\n            local shm_zones = require(\"fake_shm_zones\")\n            local name, size, isinit, isold\n            local x1 = shm_zones.x1\n\n            name, size, isinit, isold = x1:get_info()\n            ngx.say(\"name=\", name)\n            ngx.say(\"size=\", size)\n            ngx.say(\"isinit=\", isinit)\n            ngx.say(\"isold=\", isold)\n        }\n    }\n--- request\nGET /test\n--- response_body\nname=x1\nsize=1048576\nisinit=true\nisold=true\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/150-fake-delayed-load.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse lib 'lib';\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_process_enabled(1);\n#log_level('warn');\n\n#repeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\nno_long_string();\n#master_on();\n#workers(2);\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: lua code cache on\n--- http_config\n    lua_code_cache on;\n--- config\n    location = /cache_on {\n        content_by_lua_block {\n            local delayed_load = require(\"ngx.delayed_load\")\n            ngx.say(type(delayed_load.get_function))\n        }\n    }\n--- request\nGET /cache_on\n--- response_body\nfunction\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: lua code cache off\n--- http_config\n    lua_code_cache off;\n--- config\n    location = /cache_off {\n        content_by_lua_block {\n            local delayed_load = require(\"ngx.delayed_load\")\n            ngx.say(type(delayed_load.get_function))\n        }\n    }\n--- request\nGET /cache_off\n--- response_body\nfunction\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/151-initby-hup.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_CHECK_LEAK}) {\n        $SkipReason = \"unavailable for the hup tests\";\n\n    } else {\n        $ENV{TEST_NGINX_USE_HUP} = 1;\n        undef $ENV{TEST_NGINX_USE_STAP};\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n#no_diff();\n#no_long_string();\nno_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: no error in init before HUP\n--- http_config\n    init_by_lua_block {\n        foo = \"hello, FOO\"\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(foo)\n        }\n    }\n--- request\nGET /lua\n--- response_body\nhello, FOO\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: error in init after HUP (master still alive, worker process still the same as before)\n--- http_config\n    init_by_lua_block {\n        error(\"failed to init\")\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(foo)\n        }\n    }\n--- request\nGET /lua\n--- response_body\nhello, FOO\n--- error_log\nfailed to init\n--- reload_fails\n\n\n\n=== TEST 3: no error in init again\n--- http_config\n    init_by_lua_block {\n        foo = \"hello, foo\"\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(foo)\n        }\n    }\n--- request\nGET /lua\n--- response_body\nhello, foo\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: no error in init before HUP, used ngx.shared.DICT\n--- http_config\n    lua_shared_dict dogs 1m;\n\n    init_by_lua_block {\n        local dogs = ngx.shared.dogs\n        dogs:set(\"foo\", \"hello, FOO\")\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n            local foo = dogs:get(\"foo\")\n            ngx.say(foo)\n        }\n    }\n--- request\nGET /lua\n--- response_body\nhello, FOO\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: error in init after HUP, not reloaded but foo have changed.\n--- http_config\n    lua_shared_dict dogs 1m;\n\n    init_by_lua_block {\n        local dogs = ngx.shared.dogs\n        dogs:set(\"foo\", \"foo have changed\")\n\n        error(\"failed to init\")\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"HUP reload failed\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\nfoo have changed\n--- error_log\nfailed to init\n--- reload_fails\n\n\n\n=== TEST 6: no error in init again, reload success and foo still have changed.\n--- http_config\n    lua_shared_dict dogs 1m;\n\n    init_by_lua_block {\n        -- do nothing\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            local dogs = ngx.shared.dogs\n            local foo = dogs:get(\"foo\")\n            ngx.say(foo)\n            ngx.say(\"reload success\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\nfoo have changed\nreload success\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/152-timer-every.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 7 + 2);\n\n#no_diff();\nno_long_string();\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir;\n\nworker_connections(1024);\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple very\n--- config\n    location /t {\n        content_by_lua_block {\n            local begin = ngx.now()\n            local function f(premature)\n                print(\"elapsed: \", ngx.now() - begin)\n                print(\"timer prematurely expired: \", premature)\n            end\n\n            local ok, err = ngx.timer.every(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n\n            ngx.say(\"registered timer\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nregistered timer\n--- wait: 0.11\n--- no_error_log\n[error]\n[alert]\n[crit]\ntimer prematurely expired: true\n--- error_log eval\n[\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: 0\\.0(?:4[4-9]|5[0-6])\\d*, context: ngx\\.timer, client: \\d+\\.\\d+\\.\\d+\\.\\d+, server: 0\\.0\\.0\\.0:\\d+/,\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: elapsed: 0\\.(?:09|10)\\d*, context: ngx\\.timer, client: \\d+\\.\\d+\\.\\d+\\.\\d+, server: 0\\.0\\.0\\.0:\\d+/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\",\n\"timer prematurely expired: false\",\n]\n\n\n\n=== TEST 2: shared global env\n--- config\n    location /t {\n        content_by_lua_block {\n            local begin = ngx.now()\n            local function f()\n                foo = 3\n                print(\"foo in timer: \", foo)\n            end\n            local ok, err = ngx.timer.every(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.sleep(0.11)\n            ngx.say(\"foo = \", foo)\n        }\n    }\n--- request\nGET /t\n--- response_body\nfoo = 3\n--- wait: 0.12\n--- no_error_log\n[error]\n[alert]\n[crit]\n--- error_log eval\n[\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: foo in timer: 3/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 3: lua variable sharing via upvalue\n--- config\n    location /t {\n        content_by_lua_block {\n            local begin = ngx.now()\n            local foo = 0\n            local function f()\n                foo = foo + 3\n                print(\"foo in timer: \", foo)\n            end\n            local ok, err = ngx.timer.every(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.11)\n            ngx.say(\"foo = \", foo)\n        }\n    }\n--- request\nGET /t\n--- response_body\nregistered timer\nfoo = 6\n--- wait: 0.12\n--- no_error_log\n[error]\n[alert]\n[crit]\n--- error_log eval\n[\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: foo in timer: 3/,\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: foo in timer: 6/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 4: create the next timer immediately when timer start running\n--- config\n    location /t {\n        content_by_lua_block {\n            local begin = ngx.now()\n            local foo = 0\n            local function f()\n                foo = foo + 3\n                print(\"foo in timer: \", foo)\n\n                ngx.sleep(0.1)\n            end\n            local ok, err = ngx.timer.every(0.05, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.11)\n            ngx.say(\"foo = \", foo)\n        }\n    }\n--- request\nGET /t\n--- response_body\nregistered timer\nfoo = 6\n--- wait: 0.12\n--- no_error_log\n[error]\n[alert]\n[crit]\n--- error_log eval\n[\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: foo in timer: 3/,\nqr/\\[lua\\] content_by_lua\\(nginx\\.conf:\\d+\\):\\d+: foo in timer: 6/,\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 5: callback args\n--- config\n    location /t {\n        content_by_lua_block {\n            local n = 0\n\n            local function f(premature, a, b, c)\n                n = n + 1\n                print(\"the \", n, \" time, args: \", a, \", \", b, \", \", c)\n\n                a, b, c = 0, 0, 0\n            end\n\n            local ok, err = ngx.timer.every(0.05, f, 1, 2)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n\n            ngx.say(\"registered timer\")\n            ngx.sleep(0.11)\n        }\n    }\n--- request\nGET /t\n--- response_body\nregistered timer\n--- wait: 0.12\n--- no_error_log\n[error]\n[alert]\n[crit]\n--- error_log eval\n[\n\"the 1 time, args: 1, 2, nil\",\n\"the 2 time, args: 1, 2, nil\",\n\"lua ngx.timer expired\",\n\"http lua close fake http connection\"\n]\n\n\n\n=== TEST 6: memory leak check\n--- quic_max_idle_timeout: 8\n--- config\n    location /t {\n        content_by_lua_block {\n            local function f()\n                local a = 1\n                -- do nothing\n            end\n\n            for i = 1, 100 do\n                local ok, err = ngx.timer.every(0.1, f)\n                if not ok then\n                    ngx.say(\"failed to set timer: \", err)\n                    return\n                end\n            end\n\n            ngx.say(\"registered timer\")\n\n            collectgarbage(\"collect\")\n            local start = collectgarbage(\"count\")\n\n            ngx.sleep(0.21)\n\n            collectgarbage(\"collect\")\n            local growth1 = collectgarbage(\"count\") - start\n\n            ngx.sleep(0.51)\n\n            collectgarbage(\"collect\")\n            local growth2 = collectgarbage(\"count\") - start\n\n            ngx.say(\"growth1 == growth2: \", growth1 == growth2)\n        }\n    }\n--- request\nGET /t\n--- response_body\nregistered timer\ngrowth1 == growth2: true\n--- no_error_log\n[error]\n[alert]\n[crit]\n--- timeout: 8\n\n\n\n=== TEST 7: respect lua_max_pending_timers\n--- http_config\n    lua_max_pending_timers 10;\n--- config\n    location /t {\n        content_by_lua_block {\n            local function f()\n                local a = 1\n                -- do nothing\n            end\n\n            for i = 1, 11 do\n                local ok, err = ngx.timer.every(0.1, f)\n                if not ok then\n                    ngx.say(\"failed to set timer: \", err)\n                    return\n                end\n            end\n\n            ngx.say(\"registered 10 timers\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nfailed to set timer: too many pending timers\n--- no_error_log\n[error]\n[alert]\n[crit]\n\n\n\n=== TEST 8: respect lua_max_running_timers\n--- http_config\n    lua_max_pending_timers 100;\n    lua_max_running_timers 9;\n--- config\n    location /t {\n        content_by_lua_block {\n            local function f()\n                local a = 1\n                ngx.sleep(0.02)\n                -- do nothing\n            end\n\n            for i = 1, 10 do\n                local ok, err = ngx.timer.every(0.01, f)\n                if not ok then\n                    ngx.say(\"failed to set timer: \", err)\n                    return\n                end\n            end\n\n            ngx.say(\"registered 10 timers\")\n\n            ngx.sleep(0.03)\n        }\n    }\n--- request\nGET /t\n--- response_body\nregistered 10 timers\n--- no_error_log\n[error]\n[crit]\n--- error_log\nlua_max_running_timers are not enough\n\n\n\n=== TEST 9: lua_code_cache off\nFIXME: it is know that this test case leaks memory.\nso we skip it in the \"check leak\" testing mode.\n--- http_config\n    lua_code_cache off;\n--- config\n    location /t {\n        content_by_lua_block {\n            local function f()\n                local a = 1\n                -- do nothing\n            end\n\n            local ok, err = ngx.timer.every(0.01, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n\n            collectgarbage(\"collect\")\n            ngx.say(\"registered timer\")\n\n            ngx.sleep(0.03)\n\n            collectgarbage(\"collect\")\n\n            ngx.sleep(0.03)\n\n            collectgarbage(\"collect\")\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nregistered timer\nok\n--- no_error_log\n[error]\n[crit]\n--- no_check_leak\n"
  },
  {
    "path": "t/153-semaphore-hup.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse lib 'lib';\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(10140);\n#workers(1);\nlog_level('warn');\nmaster_process_enabled(1);\nrepeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 3);\n\nno_long_string();\n#no_diff();\n\nadd_block_preprocessor(sub {\n    my $block = shift;\n\n    my $http_config = $block->http_config || '';\n    $http_config .= <<'_EOC_';\n    lua_package_path \"../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;\";\n    lua_shared_dict shdict 4m;\n\n    init_by_lua_block {\n        require \"resty.core\"\n        local process = require \"ngx.process\"\n        local ok, err = process.enable_privileged_agent()\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to enable_privileged_agent: \", err)\n        end\n    }\n\n    init_worker_by_lua_block {\n        local function test(pre)\n            if pre then\n                return\n            end\n\n            local semaphore = require \"ngx.semaphore\"\n            local sem = semaphore.new()\n\n            ngx.log(ngx.ERR, \"created semaphore object\")\n\n            local function sem_wait()\n\n                local ok, err = sem:wait(100)\n                if not ok then\n                    ngx.log(ngx.ERR, \"err: \", err)\n                else\n                    ngx.log(ngx.ERR, \"wait success\")\n                end\n            end\n\n            while not ngx.worker.exiting() do\n                local co = ngx.thread.spawn(sem_wait)\n                ngx.thread.wait(co)\n            end\n        end\n\n        local ok, err = ngx.timer.at(0, test)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to create semaphore timer err: \", err)\n        end\n\n        local function reload(pre)\n            if pre then\n                return\n            end\n\n            local shdict = ngx.shared.shdict\n            local success = shdict:add(\"reloaded\", 1)\n            if not success then\n                return\n            end\n\n            ngx.log(ngx.ERR, \"try to reload nginx\")\n\n            local f, err = io.open(ngx.config.prefix() .. \"/logs/nginx.pid\", \"r\")\n            if not f then\n                ngx.say(\"failed to open nginx.pid: \", err)\n                return\n            end\n\n            local pid = f:read()\n\n            f:close()\n            os.execute(\"kill -HUP \" .. pid)\n        end\n\n        local typ = require \"ngx.process\".type\n        if typ() == \"privileged agent\" then\n            local ok, err = ngx.timer.at(0.1, reload)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to create semaphore timer err: \", err)\n            end\n        end\n    }\n_EOC_\n    $block->set_value(\"http_config\", $http_config);\n});\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: timer + reload\n--- quic_max_idle_timeout: 1.1\n--- config\n    location /test {\n        content_by_lua_block {\n            ngx.sleep(1)\n            ngx.say(\"hello\")\n        }\n    }\n--- request\nGET /test\n--- response_body\nhello\n--- grep_error_log eval: qr/created semaphore object|try to reload nginx|semaphore gc wait queue is not empty/\n--- grep_error_log_out\ncreated semaphore object\ncreated semaphore object\ntry to reload nginx\ncreated semaphore object\ncreated semaphore object\n--- skip_nginx: 3: < 1.11.2\n--- no_check_leak\n--- wait: 0.2\n\n\n\n=== TEST 2: timer + reload (lua code cache off)\n--- quic_max_idle_timeout: 1.1\n--- http_config\n    lua_code_cache off;\n--- config\n    location /test {\n        content_by_lua_block {\n            ngx.sleep(1)\n            ngx.say(\"hello\")\n        }\n    }\n--- request\nGET /test\n--- response_body\nhello\n--- grep_error_log eval: qr/created semaphore object|try to reload nginx|semaphore gc wait queue is not empty/\n--- grep_error_log_out\ncreated semaphore object\ncreated semaphore object\ntry to reload nginx\ncreated semaphore object\ncreated semaphore object\n--- skip_nginx: 3: < 1.11.2\n--- no_check_leak\n--- wait: 0.2\n"
  },
  {
    "path": "t/154-semaphore.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\nuse lib 'lib';\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(10140);\n#workers(1);\n#log_level('warn');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3) + 1;\n\nno_long_string();\n#no_diff();\n\nadd_block_preprocessor(sub {\n    my $block = shift;\n\n    my $http_config = $block->http_config || '';\n    $http_config .= <<'_EOC_';\n    lua_package_path \"../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;\";\n\n    init_by_lua_block {\n        require \"resty.core\"\n    }\n_EOC_\n    $block->set_value(\"http_config\", $http_config);\n});\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: timer + shutdown error log\n--- config\n    location /test {\n        content_by_lua_block {\n            local function test(pre)\n\n                local semaphore = require \"ngx.semaphore\"\n                local sem = semaphore.new()\n\n                local function sem_wait()\n\n                    local ok, err = sem:wait(10)\n                    if not ok then\n                        ngx.log(ngx.ERR, \"err: \", err)\n                    else\n                        ngx.log(ngx.ERR, \"wait success\")\n                    end\n                end\n\n                while not ngx.worker.exiting() do\n                    local co = ngx.thread.spawn(sem_wait)\n                    ngx.thread.wait(co)\n                end\n            end\n\n            local ok, err = ngx.timer.at(0, test)\n            ngx.log(ngx.ERR, \"hello, world\")\n            ngx.say(\"time: \", ok)\n        }\n    }\n--- request\nGET /test\n--- response_body\ntime: 1\n--- grep_error_log eval: qr/hello, world|semaphore gc wait queue is not empty/\n--- grep_error_log_out\nhello, world\n--- shutdown_error_log\n--- no_shutdown_error_log\nsemaphore gc wait queue is not empty\n\n\n\n=== TEST 2: timer + shutdown error log (lua code cache off)\nFIXME: this test case leaks memory.\n--- http_config\n    lua_code_cache off;\n--- config\n    location /test {\n        content_by_lua_block {\n            local function test(pre)\n\n                local semaphore = require \"ngx.semaphore\"\n                local sem = semaphore.new()\n\n                local function sem_wait()\n\n                    local ok, err = sem:wait(10)\n                    if not ok then\n                        ngx.log(ngx.ERR, \"err: \", err)\n                    else\n                        ngx.log(ngx.ERR, \"wait success\")\n                    end\n                end\n\n                while not ngx.worker.exiting() do\n                    local co = ngx.thread.spawn(sem_wait)\n                    ngx.thread.wait(co)\n                end\n            end\n\n            local ok, err = ngx.timer.at(0, test)\n            ngx.log(ngx.ERR, \"hello, world\")\n            ngx.say(\"time: \", ok)\n        }\n    }\n--- request\nGET /test\n--- response_body\ntime: 1\n--- grep_error_log eval: qr/hello, world|semaphore gc wait queue is not empty/\n--- grep_error_log_out\nhello, world\n--- shutdown_error_log\n--- no_shutdown_error_log\nsemaphore gc wait queue is not empty\n--- SKIP\n\n\n\n=== TEST 3: exit before post_handler was called\nIf gc is called before the ngx_http_lua_sema_handler and free the sema memory\nngx_http_lua_sema_handler would use the freed memory.\n--- config\n    location /up {\n        content_by_lua_block {\n            local semaphore = require \"ngx.semaphore\"\n            local sem = semaphore.new()\n\n            local function sem_wait()\n                ngx.log(ngx.ERR, \"ngx.sem wait start\")\n                local ok, err = sem:wait(10)\n                if not ok then\n                    ngx.log(ngx.ERR, \"ngx.sem wait err: \", err)\n                else\n                    ngx.log(ngx.ERR, \"ngx.sem wait success\")\n                end\n            end\n            local co = ngx.thread.spawn(sem_wait)\n            ngx.log(ngx.ERR, \"ngx.sem post start\")\n            sem:post()\n            ngx.log(ngx.ERR, \"ngx.sem post end\")\n            ngx.say(\"hello\")\n            ngx.exit(200)\n            ngx.say(\"not reach here\")\n        }\n    }\n\n    location /t {\n        content_by_lua_block {\n            local res = ngx.location.capture(\"/up\")\n            collectgarbage()\n            ngx.print(res.body)\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nhello\n--- grep_error_log eval: qr/(ngx.sem .*?,|http close request|semaphore handler: wait queue: empty, resource count: 1|in lua gc, semaphore)/\n--- grep_error_log_out\nngx.sem wait start,\nngx.sem post start,\nngx.sem post end,\nin lua gc, semaphore\nhttp close request\n"
  },
  {
    "path": "t/155-tls13.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(3);\n\n# All these tests need to have new openssl\nmy $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';\nmy $openssl_version = eval { `$NginxBinary -V 2>&1` };\n\nif ($openssl_version =~ m/built with OpenSSL (0\\S*|1\\.0\\S*|1\\.1\\.0\\S*)/) {\n    plan(skip_all => \"too old OpenSSL, need >= 1.1.1, was $1\");\n} else {\n    plan tests => repeat_each() * (blocks() * 5);\n}\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\nsub read_file {\n    my $infile = shift;\n    open my $in, $infile\n        or die \"cannot open $infile for reading: $!\";\n    my $cert = do { local $/; <$in> };\n    close $in;\n    $cert;\n}\n\nour $TestCertificate = read_file(\"t/cert/test.crt\");\nour $TestCertificateKey = read_file(\"t/cert/test.key\");\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: handshake, TLSv1.3\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate ../html/test.crt;\n        ssl_certificate_key ../html/test.key;\n        ssl_protocols TLSv1.2 TLSv1.3;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../html/test.crt;\n    lua_ssl_protocols TLSv1.2 TLSv1.3;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(3000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                else\n                    ngx.say(\"ssl handshake: \", type(sess))\n                end\n            end  -- do\n            collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\n\n--- user_files eval\n\">>> test.key\n$::TestCertificateKey\n>>> test.crt\n$::TestCertificate\"\n--- error_log\nSSL: TLSv1.3,\n--- no_error_log\n[error]\n[alert]\n--- timeout: 5\n"
  },
  {
    "path": "t/156-slow-network.t",
    "content": "BEGIN {\n    if (!defined $ENV{LD_PRELOAD}) {\n        $ENV{LD_PRELOAD} = '';\n    }\n\n    if ($ENV{LD_PRELOAD} !~ /\\bmockeagain\\.so\\b/) {\n        $ENV{LD_PRELOAD} = \"mockeagain.so $ENV{LD_PRELOAD}\";\n    }\n\n    if ($ENV{MOCKEAGAIN} eq 'r') {\n        $ENV{MOCKEAGAIN} = 'rw';\n\n    } else {\n        $ENV{MOCKEAGAIN} = 'w';\n    }\n\n    $ENV{TEST_NGINX_EVENT_TYPE} = 'poll';\n    delete($ENV{TEST_NGINX_USE_HTTP2});\n}\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 4);\n\nadd_block_preprocessor(sub {\n    my $block = shift;\n\n    if (!defined $block->error_log) {\n        $block->set_value(\"no_error_log\", \"[error]\");\n    }\n\n    if (!defined $block->request) {\n        $block->set_value(\"request\", \"GET /t\");\n    }\n\n});\n\n\nlog_level(\"debug\");\nno_long_string();\n#no_diff();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: receiveany returns anything once socket receives\n--- config\n    server_tokens off;\n    location = /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(500)\n            assert(sock:connect(\"127.0.0.1\", ngx.var.port))\n            local req = {\n                'GET /foo HTTP/1.0\\r\\n',\n                'Host: localhost\\r\\n',\n                'Connection: close\\r\\n\\r\\n',\n            }\n            local ok, err = sock:send(req)\n            if not ok then\n                ngx.say(\"send request failed: \", err)\n                return\n            end\n\n\n            -- skip http header\n            while true do\n                local data, err, _ = sock:receive('*l')\n                if err then\n                    ngx.say('unexpected error occurs when receiving http head: ' .. err)\n                    return\n                end\n                if #data == 0 then -- read last line of head\n                    break\n                end\n            end\n\n            -- receive http body\n            while true do\n                local data, err = sock:receiveany(1024)\n                if err then\n                    if err ~= 'closed' then\n                        ngx.say('unexpected err: ', err)\n                    end\n                    break\n                end\n                ngx.say(data)\n            end\n\n            sock:close()\n        }\n    }\n\n    location = /foo {\n        content_by_lua_block {\n            local resp = {\n                '1',\n                'hello',\n            }\n\n            local length = 0\n            for _, v in ipairs(resp) do\n                length = length + #v\n            end\n\n            -- flush http header\n            ngx.header['Content-Length'] = length\n            ngx.flush(true)\n            ngx.sleep(0.01)\n\n            -- send http body bytes by bytes\n            for _, v in ipairs(resp) do\n                ngx.print(v)\n                ngx.flush(true)\n                ngx.sleep(0.01)\n            end\n        }\n    }\n\n--- response_body\n1\nh\ne\nl\nl\no\n--- grep_error_log eval\nqr/lua tcp socket read any/\n--- grep_error_log_out\nlua tcp socket read any\nlua tcp socket read any\nlua tcp socket read any\nlua tcp socket read any\nlua tcp socket read any\nlua tcp socket read any\nlua tcp socket read any\n"
  },
  {
    "path": "t/157-socket-keepalive-hup.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_CHECK_LEAK}) {\n        $SkipReason = \"unavailable for the hup tests\";\n\n    } elsif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n        #os.execute(\"kill -HUP \" .. pid)\n        $SkipReason = \"send HUP relaod signal by self make two workers with same id\";\n\n    } else {\n        $ENV{TEST_NGINX_USE_HUP} = 1;\n        undef $ENV{TEST_NGINX_USE_STAP};\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 8);\n\n#no_diff();\nno_long_string();\n\nworker_connections(1024);\nrun_tests();\n\n__DATA__\n\n=== TEST 1: exiting\n--- config\n    location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n\n        content_by_lua_block {\n            local pidfile = ngx.config.prefix() .. \"/logs/nginx.pid\"\n            local f, err = io.open(pidfile, \"r\")\n            if not f then\n                ngx.say(\"failed to open nginx.pid: \", err)\n                return\n            end\n\n            local pid = f:read()\n            -- ngx.say(\"master pid: [\", pid, \"]\")\n\n            f:close()\n\n            local i = 0\n            local port = ngx.var.port\n\n            local function f(premature)\n                print(\"timer prematurely expired: \", premature)\n\n                local sock = ngx.socket.tcp()\n\n                local ok, err = sock:connect(\"127.0.0.1\", port)\n                if not ok then\n                    print(\"failed to connect: \", err)\n                    return\n                end\n\n                local ok, err = sock:setkeepalive()\n                if not ok then\n                    print(\"failed to setkeepalive: \", err)\n                    return\n                end\n\n                print(\"setkeepalive successfully\")\n            end\n            local ok, err = ngx.timer.at(3, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.say(\"registered timer\")\n            os.execute(\"kill -HUP \" .. pid)\n        }\n    }\n--- request\nGET /t\n\n--- response_body\nregistered timer\n\n--- wait: 0.3\n--- no_error_log\n[error]\n[alert]\n[crit]\n--- error_log\ntimer prematurely expired: true\nsetkeepalive successfully\nlua tcp socket set keepalive while process exiting, closing connection\n"
  },
  {
    "path": "t/158-global-var.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nlog_level('debug');\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 14);\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n\nno_long_string();\n\nsub read_file {\n    my $infile = shift;\n    open my $in, $infile\n        or die \"cannot open $infile for reading: $!\";\n    my $cert = do { local $/; <$in> };\n    close $in;\n    $cert;\n}\n\nour $TestCertificate = read_file(\"t/cert/test.crt\");\nour $TestCertificateKey = read_file(\"t/cert/test.key\");\n\nadd_block_preprocessor(sub {\n    my $block = shift;\n\n    if (!defined $block->error_log) {\n        $block->set_value(\"no_error_log\", \"[error]\");\n    }\n\n    if (!defined $block->request) {\n        $block->set_value(\"request\", \"GET /t\");\n    }\n\n});\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: set_by_lua\n--- config\n    location /t {\n        set_by_lua_block $res {\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.WARN, \"old foo: \", foo)\n                foo = foo + 1\n            end\n            return foo\n        }\n        echo $res;\n    }\n--- response_body_like chomp\n\\A[12]\\n\\z\n--- grep_error_log eval\nqr/(old foo: \\d+|\\[\\w+\\].*?writing a global Lua variable \\('[^'\\s]+'\\)|set_by_lua\\(nginx.conf:\\d+\\):\\d+: in main chunk, )/\n--- grep_error_log_out eval\n[qr/\\A\\[warn\\] .*?writing a global Lua variable \\('foo'\\)\nset_by_lua\\(nginx.conf:40\\):3: in main chunk, \\n\\z/, \"old foo: 1\\n\"]\n\n\n\n=== TEST 2: rewrite_by_lua\n--- config\n    location /t {\n        rewrite_by_lua_block {\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.WARN, \"old foo: \", foo)\n                foo = foo + 1\n            end\n            ngx.say(foo)\n        }\n    }\n--- response_body_like chomp\n\\A[12]\\n\\z\n--- grep_error_log eval\nqr/(old foo: \\d+|\\[\\w+\\].*?writing a global Lua variable \\('[^'\\s]+'\\)|\\w+_by_lua\\(.*?\\):\\d+: in main chunk, )/\n--- grep_error_log_out eval\n[qr/\\A\\[warn\\] .*?writing a global Lua variable \\('foo'\\)\nrewrite_by_lua\\(nginx\\.conf:\\d+\\):\\d+: in main chunk, \\n\\z/, \"old foo: 1\\n\"]\n\n\n\n=== TEST 3: access_by_lua\n--- config\n    location /t {\n        access_by_lua_block {\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.WARN, \"old foo: \", foo)\n                foo = foo + 1\n            end\n            ngx.say(foo)\n        }\n    }\n--- response_body_like chomp\n\\A[12]\\n\\z\n--- grep_error_log eval\nqr/(old foo: \\d+|\\[\\w+\\].*?writing a global Lua variable \\('[^'\\s]+'\\)|\\w+_by_lua\\(.*?\\):\\d+: in main chunk, )/\n--- grep_error_log_out eval\n[qr/\\A\\[warn\\] .*?writing a global Lua variable \\('foo'\\)\naccess_by_lua\\(nginx\\.conf:\\d+\\):\\d+: in main chunk, \\n\\z/, \"old foo: 1\\n\"]\n\n\n\n=== TEST 4: content_by_lua\n--- config\n    location /t {\n        content_by_lua_block {\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.WARN, \"old foo: \", foo)\n                foo = foo + 1\n            end\n            ngx.say(foo)\n        }\n    }\n--- response_body_like chomp\n\\A[12]\\n\\z\n--- grep_error_log eval\nqr/(old foo: \\d+|\\[\\w+\\].*?writing a global Lua variable \\('[^'\\s]+'\\)|\\w+_by_lua\\(.*?\\):\\d+: in main chunk, )/\n--- grep_error_log_out eval\n[qr/\\A\\[warn\\] .*?writing a global Lua variable \\('foo'\\)\ncontent_by_lua\\(nginx\\.conf:\\d+\\):\\d+: in main chunk, \\n\\z/, \"old foo: 1\\n\"]\n\n\n\n=== TEST 5: header_filter_by_lua\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.say(foo)\n        }\n        header_filter_by_lua_block {\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.WARN, \"old foo: \", foo)\n                foo = foo + 1\n            end\n        }\n    }\n--- response_body_like chomp\n\\A(?:nil|1)\\n\\z\n--- grep_error_log eval\nqr/(old foo: \\d+|\\[\\w+\\].*?writing a global Lua variable \\('[^'\\s]+'\\)|\\w+_by_lua\\(nginx\\.conf:\\d+\\):\\d+: in main chunk, )/\n--- grep_error_log_out eval\n[qr/\\A\\[warn\\] .*?writing a global Lua variable \\('foo'\\)\nheader_filter_by_lua\\(nginx.conf:43\\):3: in main chunk, \\n\\z/, \"old foo: 1\\n\"]\n\n\n\n=== TEST 6: body_filter_by_lua\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.say(foo)\n        }\n        body_filter_by_lua_block {\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.WARN, \"old foo: \", foo)\n                foo = foo + 1\n            end\n        }\n    }\n--- response_body_like chomp\n\\A(?:nil|2)\\n\\z\n--- grep_error_log eval\nqr/(old foo: \\d+|\\[\\w+\\].*?writing a global Lua variable \\('[^'\\s]+'\\)|\\w+_by_lua\\(nginx\\.conf:\\d+\\):\\d+: in main chunk,)/\n--- grep_error_log_out eval\n[qr/\\[warn\\] .*?writing a global Lua variable \\('foo'\\)\nbody_filter_by_lua\\(nginx.conf:43\\):3: in main chunk,\nold foo: 1\\n\\z/, \"old foo: 2\\nold foo: 3\\n\"]\n\n\n\n=== TEST 7: log_by_lua\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.say(foo)\n        }\n        log_by_lua_block {\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.WARN, \"old foo: \", foo)\n                foo = foo + 1\n            end\n        }\n    }\n--- response_body_like chomp\n\\A(?:nil|1)\\n\\z\n--- grep_error_log eval\nqr/(old foo: \\d+|\\[\\w+\\].*?writing a global Lua variable \\('[^'\\s]+'\\)|\\w+_by_lua\\(.*?\\):\\d+: in main chunk)/\n--- grep_error_log_out eval\n[qr/\\A\\[warn\\] .*?writing a global Lua variable \\('foo'\\)\nlog_by_lua\\(nginx\\.conf:\\d+\\):\\d+: in main chunk\\n\\z/, \"old foo: 1\\n\"]\n\n\n\n=== TEST 8: ssl_certificate_by_lua\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_certificate_by_lua_block {\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.WARN, \"old foo: \", foo)\n                foo = foo + 1\n            end\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            content_by_lua_block {\n                ngx.say(\"foo: \", foo)\n            }\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                -- ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                -- ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                -- ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    local m, err = ngx.re.match(line, \"^foo: (.*)$\", \"jo\")\n                    if err then\n                        ngx.say(\"failed to match line: \", err)\n                    end\n\n                    if m and m[1] then\n                        ngx.print(m[1])\n                    end\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"done\")\n            end  -- do\n        }\n    }\n\n--- response_body_like chomp\n\\A[12]done\\n\\z\n--- grep_error_log eval\nqr/(old foo: \\d+|\\[\\w+\\].*?writing a global Lua variable \\('[^'\\s]+'\\)|\\w+_by_lua\\(nginx.conf:\\d+\\):\\d+: in main chunk)/\n--- grep_error_log_out eval\n[qr/\\A\\[warn\\] .*?writing a global Lua variable \\('foo'\\)\nssl_certificate_by_lua\\(nginx.conf:28\\):3: in main chunk\\n\\z/, \"old foo: 1\\n\"]\n\n\n\n=== TEST 9: timer\n--- config\n    location /t {\n        content_by_lua_block {\n            local function f()\n                if not foo then\n                    foo = 1\n                else\n                    ngx.log(ngx.WARN, \"old foo: \", foo)\n                    foo = foo + 1\n                end\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.sleep(0.01)\n            ngx.say(foo)\n        }\n    }\n--- response_body_like chomp\n\\A[12]\\n\\z\n--- grep_error_log eval\nqr/(old foo: \\d+|\\[\\w+\\].*?writing a global Lua variable \\('[^'\\s]+'\\)|\\w+_by_lua\\(.*?\\):\\d+: in\\b)/\n--- grep_error_log_out eval\n[qr/\\A\\[warn\\] .*?writing a global Lua variable \\('foo'\\)\ncontent_by_lua\\(nginx\\.conf:\\d+\\):\\d+: in\\n\\z/, \"old foo: 1\\n\"]\n\n\n\n=== TEST 10: init_by_lua\n--- http_config\n    init_by_lua_block {\n        foo = 1\n    }\n--- config\n    location /t {\n        content_by_lua_block {\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.WARN, \"old foo: \", foo)\n                foo = foo + 1\n            end\n            ngx.say(foo)\n        }\n    }\n--- response_body_like chomp\n\\A[23]\\n\\z\n--- grep_error_log eval: qr/old foo: \\d+/\n--- grep_error_log_out eval\n[\"old foo: 1\\n\", \"old foo: 2\\n\"]\n\n\n\n=== TEST 11: init_worker_by_lua\n--- http_config\n    init_worker_by_lua_block {\n        if not foo then\n            foo = 1\n        else\n            ngx.log(ngx.WARN, \"old foo: \", foo)\n            foo = foo + 1\n        end\n    }\n--- config\n    location /t {\n        content_by_lua_block {\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.WARN, \"old foo: \", foo)\n                foo = foo + 1\n            end\n            ngx.say(foo)\n        }\n    }\n--- response_body_like chomp\n\\A[23]\\n\\z\n--- grep_error_log eval: qr/old foo: \\d+/\n--- grep_error_log_out eval\n[\"old foo: 1\\n\", \"old foo: 2\\n\"]\n\n\n\n=== TEST 12: init_by_lua + init_worker_by_lua\n--- http_config\n    init_by_lua_block {\n        if not foo then\n            foo = 1\n        else\n            ngx.log(ngx.WARN, \"old foo: \", foo)\n            foo = foo + 1\n        end\n    }\n    init_worker_by_lua_block {\n        if not foo then\n            foo = 1\n        else\n            ngx.log(ngx.WARN, \"old foo: \", foo)\n            foo = foo + 1\n        end\n    }\n--- config\n    location /t {\n        content_by_lua_block {\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.WARN, \"old foo: \", foo)\n                foo = foo + 1\n            end\n            ngx.say(foo)\n        }\n    }\n--- response_body_like chomp\n\\A[34]\\n\\z\n--- grep_error_log eval: qr/old foo: \\d+/\n--- grep_error_log_out eval\n[\"old foo: 1\\nold foo: 2\\n\", \"old foo: 3\\n\"]\n\n\n\n=== TEST 13: don't show warn messages in init/init_worker\n--- http_config\n    init_by_lua_block {\n        foo = 1\n    }\n\n    init_worker_by_lua_block {\n        bar = 2\n    }\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.say(foo)\n            ngx.say(bar)\n        }\n    }\n--- response_body\n1\n2\n--- no_error_log\nsetting global variable\n\n\n\n=== TEST 14: uthread\n--- config\n    location /t {\n        content_by_lua_block {\n            local function f()\n                if not foo then\n                    foo = 1\n                else\n                    ngx.log(ngx.WARN, \"old foo: \", foo)\n                    foo = foo + 1\n                end\n            end\n            local ok, err = ngx.thread.spawn(f)\n            if not ok then\n                ngx.say(\"failed to set timer: \", err)\n                return\n            end\n            ngx.sleep(0.01)\n            ngx.say(foo)\n        }\n    }\n--- response_body_like chomp\n\\A[12]\\n\\z\n--- grep_error_log eval\nqr/(old foo: \\d+|writing a global Lua variable \\('\\w+'\\))/\n--- grep_error_log_out eval\n[\"writing a global Lua variable \\('foo'\\)\\n\", \"old foo: 1\\n\"]\n\n\n\n=== TEST 15: balancer_by_lua\n--- http_config\n    upstream backend {\n        server 0.0.0.1;\n        balancer_by_lua_block {\n            if not foo then\n                foo = 1\n            else\n                ngx.log(ngx.WARN, \"old foo: \", foo)\n                foo = foo + 1\n            end\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://backend;\n    }\n--- response_body_like: 502 Bad Gateway\n--- error_code: 502\n--- error_log eval\nqr/\\[crit\\].*?\\Qconnect() to 0.0.0.1:80 failed\\E/\n--- grep_error_log eval: qr/(old foo: \\d+|writing a global Lua variable \\('\\w+'\\))/\n--- grep_error_log_out eval\n[\"writing a global Lua variable \\('foo'\\)\\n\", \"old foo: 1\\n\"]\n"
  },
  {
    "path": "t/159-sa-restart.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nadd_block_preprocessor(sub {\n    my $block = shift;\n\n    my $http_config = $block->http_config || '';\n\n    $http_config .= <<_EOC_;\n    init_by_lua_block {\n        function test_sa_restart()\n            local signals = {\n                --\"HUP\",\n                --\"INFO\",\n                --\"XCPU\",\n                --\"USR1\",\n                --\"USR2\",\n                \"ALRM\",\n                --\"INT\",\n                \"IO\",\n                \"CHLD\",\n                --\"WINCH\",\n            }\n\n            for _, signame in ipairs(signals) do\n                local cmd = string.format(\"kill -s %s %d && sleep 0.01\",\n                                          signame, ngx.worker.pid())\n                local err = select(2, io.popen(cmd):read(\"*a\"))\n                if err then\n                    error(\"SIG\" .. signame .. \" caused: \" .. err)\n                end\n            end\n        end\n    }\n_EOC_\n\n    $block->set_value(\"http_config\", $http_config);\n\n    if (!defined $block->config) {\n        my $config = <<_EOC_;\n        location /t {\n            echo ok;\n        }\n_EOC_\n\n        $block->set_value(\"config\", $config);\n    }\n\n    if (!defined $block->request) {\n        $block->set_value(\"request\", \"GET /t\");\n    }\n\n    if (!defined $block->response_body) {\n        $block->set_value(\"ignore_response_body\");\n    }\n\n    if (!defined $block->no_error_log) {\n        $block->set_value(\"no_error_log\", \"[error]\");\n    }\n});\n\nplan tests => repeat_each() * (blocks() * 2 + 1);\n\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: lua_sa_restart default - sets SA_RESTART in init_worker_by_lua*\n--- http_config\n    init_worker_by_lua_block {\n        test_sa_restart()\n    }\n\n\n\n=== TEST 2: lua_sa_restart off - does not set SA_RESTART\n--- http_config\n    lua_sa_restart off;\n\n    init_worker_by_lua_block {\n        test_sa_restart()\n    }\n--- no_error_log\n[crit]\n--- error_log\nInterrupted system call\n\n\n\n=== TEST 3: lua_sa_restart on (default) - sets SA_RESTART if no init_worker_by_lua* phase is defined\n--- config\n    location /t {\n        content_by_lua_block {\n            test_sa_restart()\n        }\n    }\n\n\n\n=== TEST 4: lua_sa_restart on (default) - SA_RESTART is effective in rewrite_by_lua*\n--- config\n    location /t {\n        rewrite_by_lua_block {\n            test_sa_restart()\n        }\n\n        echo ok;\n    }\n\n\n\n=== TEST 5: lua_sa_restart on (default) - SA_RESTART is effective in access_by_lua*\n--- config\n    location /t {\n        access_by_lua_block {\n            test_sa_restart()\n        }\n\n        echo ok;\n    }\n\n\n\n=== TEST 6: lua_sa_restart on (default) - SA_RESTART is effective in content_by_lua*\n--- config\n    location /t {\n        content_by_lua_block {\n            test_sa_restart()\n        }\n    }\n\n\n\n=== TEST 7: lua_sa_restart on (default) - SA_RESTART is effective in header_filter_by_lua*\n--- config\n    location /t {\n        echo ok;\n\n        header_filter_by_lua_block {\n            test_sa_restart()\n        }\n    }\n\n\n\n=== TEST 8: lua_sa_restart on (default) - SA_RESTART is effective in body_filter_by_lua*\n--- config\n    location /t {\n        echo ok;\n\n        body_filter_by_lua_block {\n            test_sa_restart()\n        }\n    }\n\n\n\n=== TEST 9: lua_sa_restart on (default) - SA_RESTART is effective in log_by_lua*\n--- config\n    location /t {\n        echo ok;\n\n        log_by_lua_block {\n            test_sa_restart()\n        }\n    }\n\n\n\n=== TEST 10: lua_sa_restart on (default) - SA_RESTART is effective in timer phase\n--- config\n    location /t {\n        echo ok;\n\n        log_by_lua_block {\n            ngx.timer.at(0, test_sa_restart)\n        }\n    }\n"
  },
  {
    "path": "t/160-disable-init-by-lua.t",
    "content": "use Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n\nmy $html_dir = $ENV{TEST_NGINX_HTML_DIR};\nmy $http_config = <<_EOC_;\n    init_by_lua_block {\n        function set_up_ngx_tmp_conf(conf)\n            if conf == nil then\n                conf = [[\n                    # to prevent the test process from overwriting the\n                    # original pid file\n                    pid logs/test_nginx.pid;\n                    events {\n                        worker_connections 64;\n                    }\n                    http {\n                        init_by_lua_block {\n                            ngx.log(ngx.ERR, \"run init_by_lua\")\n                        }\n                    }\n                ]]\n            end\n\n            assert(os.execute(\"mkdir -p $html_dir/logs\"))\n\n            local conf_file = \"$html_dir/nginx.conf\"\n            local f, err = io.open(conf_file, \"w\")\n            if not f then\n                ngx.log(ngx.ERR, err)\n                return\n            end\n\n            assert(f:write(conf))\n            f:close()\n\n            return conf_file\n        end\n\n        function get_ngx_bin_path()\n            local ffi = require \"ffi\"\n            ffi.cdef[[char **ngx_argv;]]\n            return ffi.string(ffi.C.ngx_argv[0])\n        end\n    }\n_EOC_\n\nadd_block_preprocessor(sub {\n    my $block = shift;\n\n    if (!defined $block->http_config) {\n        $block->set_value(\"http_config\", $http_config);\n    }\n\n    if (!defined $block->request) {\n        $block->set_value(\"request\", \"GET /t\");\n    }\n});\n\nenv_to_nginx(\"PATH\");\nlog_level(\"warn\");\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: ensure init_by_lua* is not run in signaller process\n--- config\n    location = /t {\n        content_by_lua_block {\n            local conf_file = set_up_ngx_tmp_conf()\n            local nginx = get_ngx_bin_path()\n\n            local cmd = nginx .. \" -p $TEST_NGINX_HTML_DIR -c \" .. conf_file .. \" -s reopen\"\n            local p, err = io.popen(cmd)\n            if not p then\n                ngx.log(ngx.ERR, err)\n                return\n            end\n\n            local out, err = p:read(\"*a\")\n            if not out then\n                ngx.log(ngx.ERR, err)\n\n            else\n                ngx.log(ngx.WARN, out)\n            end\n            p:close()\n            collectgarbage(\"collect\")\n        }\n    }\n--- error_log\nfailed (2: No such file or directory)\n--- no_error_log eval\nqr/\\[error\\] .*? init_by_lua:\\d+: run init_by_lua/\n\n\n\n=== TEST 2: init_by_lua* does not run when testing Nginx configuration\n--- config\n    location = /t {\n        content_by_lua_block {\n            local conf_file = set_up_ngx_tmp_conf()\n            local nginx = get_ngx_bin_path()\n\n            local cmd = nginx .. \" -p $TEST_NGINX_HTML_DIR -c \" .. conf_file .. \" -t\"\n            local p, err = io.popen(cmd)\n            if not p then\n                ngx.log(ngx.ERR, err)\n                return\n            end\n\n            local out, err = p:read(\"*a\")\n            if not out then\n                ngx.log(ngx.ERR, err)\n\n            else\n                ngx.log(ngx.WARN, out)\n            end\n            p:close()\n\n            local cmd = nginx .. \" -p $TEST_NGINX_HTML_DIR -c \" .. conf_file .. \" -T\"\n            local p, err = io.popen(cmd)\n            if not p then\n                ngx.log(ngx.ERR, err)\n                return\n            end\n\n            local out, err = p:read(\"*a\")\n            if not out then\n                ngx.log(ngx.ERR, err)\n\n            else\n                ngx.log(ngx.WARN, out)\n            end\n            p:close()\n            collectgarbage(\"collect\")\n        }\n    }\n--- error_log\ntest is successful\n--- no_error_log eval\nqr/\\[error\\] .*? init_by_lua:\\d+: run init_by_lua/\n\n\n\n=== TEST 3: init_by_lua* does not run when testing Nginx configuration which contains 'lua_shared_dict' (GitHub #1462)\n--- config\n    location = /t {\n        content_by_lua_block {\n            local conf = [[\n                pid logs/test_nginx.pid;\n                events {\n                    worker_connections 64;\n                }\n                http {\n                    lua_shared_dict test 64k;\n                    init_by_lua_block {\n                        ngx.log(ngx.ERR, \"run init_by_lua with lua_shared_dict\")\n                    }\n                }\n            ]]\n            local conf_file = set_up_ngx_tmp_conf(conf)\n            local nginx = get_ngx_bin_path()\n\n            local cmd = nginx .. \" -p $TEST_NGINX_HTML_DIR -c \" .. conf_file .. \" -t\"\n            local p, err = io.popen(cmd)\n            if not p then\n                ngx.log(ngx.ERR, err)\n                return\n            end\n\n            local out, err = p:read(\"*a\")\n            if not out then\n                ngx.log(ngx.ERR, err)\n\n            else\n                ngx.log(ngx.WARN, out)\n            end\n            p:close()\n\n            local cmd = nginx .. \" -p $TEST_NGINX_HTML_DIR -c \" .. conf_file .. \" -T\"\n            local p, err = io.popen(cmd)\n            if not p then\n                ngx.log(ngx.ERR, err)\n                return\n            end\n\n            local out, err = p:read(\"*a\")\n            if not out then\n                ngx.log(ngx.ERR, err)\n\n            else\n                ngx.log(ngx.WARN, out)\n            end\n            p:close()\n            collectgarbage(\"collect\")\n        }\n    }\n--- error_log\ntest is successful\n--- no_error_log eval\nqr/\\[error\\] .*? init_by_lua:\\d+: run init_by_lua with lua_shared_dict/\n"
  },
  {
    "path": "t/161-load-resty-core.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\nour $HtmlDir = html_dir;\n\nadd_block_preprocessor(sub {\n    my $block = shift;\n\n    if (!defined $block->request) {\n        $block->set_value(\"request\", \"GET /t\");\n    }\n\n    if (!defined $block->no_error_log) {\n        $block->set_value(\"no_error_log\", \"[error]\");\n    }\n});\n\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: resty.core is automatically loaded in the Lua VM\n--- config\n    location = /t {\n        content_by_lua_block {\n            local loaded_resty_core = package.loaded[\"resty.core\"]\n            local resty_core = require \"resty.core\"\n\n            ngx.say(\"resty.core loaded: \", loaded_resty_core == resty_core)\n        }\n    }\n--- response_body\nresty.core loaded: true\n\n\n\n=== TEST 2: resty.core is automatically loaded in the Lua VM when 'lua_shared_dict' is used\n--- http_config\n    lua_shared_dict dogs 128k;\n--- config\n    location = /t {\n        content_by_lua_block {\n            local loaded_resty_core = package.loaded[\"resty.core\"]\n            local resty_core = require \"resty.core\"\n\n            ngx.say(\"resty.core loaded: \", loaded_resty_core == resty_core)\n        }\n    }\n--- response_body\nresty.core loaded: true\n\n\n\n=== TEST 3: resty.core is automatically loaded in the Lua VM with 'lua_code_cache off'\n--- http_config\n    lua_code_cache off;\n--- config\n    location = /t {\n        content_by_lua_block {\n            local loaded_resty_core = package.loaded[\"resty.core\"]\n            local resty_core = require \"resty.core\"\n\n            ngx.say(\"resty.core loaded: \", loaded_resty_core == resty_core)\n        }\n    }\n--- response_body\nresty.core loaded: true\n\n\n\n=== TEST 4: resty.core loading honors the lua_package_path directive\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;;';\"\n--- config\n    location = /t {\n        content_by_lua_block {\n            local loaded_resty_core = package.loaded[\"resty.core\"]\n            local resty_core = require \"resty.core\"\n\n            ngx.say(\"resty.core loaded: \", loaded_resty_core == resty_core)\n\n            resty_core.go()\n        }\n    }\n--- response_body\nresty.core loaded: true\nloaded from html dir\n--- user_files\n>>> resty/core.lua\nreturn {\n    go = function ()\n        ngx.say(\"loaded from html dir\")\n    end\n}\n\n\n\n=== TEST 5: resty.core not loading aborts the initialization\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;';\"\n--- config\n    location = /t {\n        return 200;\n    }\n--- must_die\n--- error_log eval\nqr/\\[alert\\] .*? failed to load the 'resty\\.core' module .*? \\(reason: module 'resty\\.core' not found:/\n\n\n\n=== TEST 6: resty.core not loading produces an error with 'lua_code_cache off'\n--- http_config\n    lua_code_cache off;\n\n    init_by_lua_block {\n        package.path = \"\"\n    }\n--- config\n    location = /t {\n        content_by_lua_block {\n            ngx.say(\"ok\")\n        }\n    }\n--- error_code: 500\n--- error_log eval\nqr/\\[error\\] .*? failed to load the 'resty\\.core' module .*? \\(reason: module 'resty\\.core' not found:/\n--- no_error_log eval\nqr/\\[alert\\] .*? failed to load the 'resty\\.core' module/\n\n\n\n=== TEST 7: lua_load_resty_core logs a deprecation warning when specified (on)\n--- http_config\n    lua_load_resty_core on;\n--- config\n    location = /t {\n        return 200;\n    }\n--- grep_error_log eval: qr/\\[warn\\] .*? lua_load_resty_core is deprecated.*/\n--- grep_error_log_out eval\n[\nqr/\\[warn\\] .*? lua_load_resty_core is deprecated \\(the lua-resty-core library is required since ngx_lua v0\\.10\\.16\\) in .*?nginx\\.conf:\\d+/,\n\"\"\n]\n\n\n\n=== TEST 8: lua_load_resty_core logs a deprecation warning when specified (off)\n--- http_config\n    lua_load_resty_core off;\n--- config\n    location = /t {\n        return 200;\n    }\n--- grep_error_log eval: qr/\\[warn\\] .*? lua_load_resty_core is deprecated.*/\n--- grep_error_log_out eval\n[\nqr/\\[warn\\] .*? lua_load_resty_core is deprecated \\(the lua-resty-core library is required since ngx_lua v0\\.10\\.16\\) in .*?nginx\\.conf:\\d+/,\n\"\"\n]\n"
  },
  {
    "path": "t/162-exit-worker.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nmaster_on();\nrepeat_each(2);\n\n# NB: the shutdown_error_log block is independent from repeat times\nplan tests => repeat_each() * (blocks() * 2 + 1) + 15;\n\n#log_level(\"warn\");\nno_long_string();\nour $HtmlDir = html_dir;\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple exit_worker_by_lua_block\n--- http_config\n    exit_worker_by_lua_block {\n        ngx.log(ngx.NOTICE, \"log from exit_worker_by_lua_block\")\n    }\n--- config\n    location /t {\n        echo \"ok\";\n    }\n--- request\nGET /t\n--- response_body\nok\n--- shutdown_error_log\nlog from exit_worker_by_lua_block\n\n\n\n=== TEST 2: simple exit_worker_by_lua_file\n--- http_config\n    exit_worker_by_lua_file html/exit_worker.lua;\n--- config\n    location /t {\n        echo \"ok\";\n    }\n--- user_files\n>>> exit_worker.lua\nngx.log(ngx.NOTICE, \"log from exit_worker_by_lua_file\")\n--- request\nGET /t\n--- response_body\nok\n--- shutdown_error_log\nlog from exit_worker_by_lua_file\n\n\n\n=== TEST 3: exit_worker_by_lua (require a global table)\n--- http_config eval\n    qq{lua_package_path '$::HtmlDir/?.lua;;';\n        exit_worker_by_lua_block {\n            foo = require(\"foo\")\n            ngx.log(ngx.NOTICE, foo.bar)\n        }}\n--- config\n    location /t {\n        content_by_lua_block {\n            foo = require(\"foo\")\n            foo.bar = \"hello, world\"\n            ngx.say(\"ok\")\n        }\n    }\n--- user_files\n>>> foo.lua\nreturn {}\n--- request\nGET /t\n--- response_body\nok\n--- shutdown_error_log\nhello, world\n\n\n\n=== TEST 4: ngx.timer is not allow\n--- http_config\n    exit_worker_by_lua_block {\n        local function bar()\n            ngx.log(ngx.ERR, \"run the timer!\")\n        end\n\n        local ok, err = ngx.timer.at(0, bar)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to create timer: \", err)\n        else\n            ngx.log(ngx.NOTICE, \"success\")\n        end\n    }\n--- config\n    location /t {\n        echo \"ok\";\n    }\n--- request\nGET /t\n--- response_body\nok\n--- shutdown_error_log\nAPI disabled in the context of exit_worker_by_lua*\n\n\n\n=== TEST 5: exit_worker_by_lua use shdict\n--- http_config\n    lua_shared_dict dog 1m;\n    exit_worker_by_lua_block {\n        local dog = ngx.shared.dog\n        local val, err = dog:get(\"foo\")\n        if not val then\n            ngx.log(ngx.ERR, \"failed get shdict: \", err)\n        else\n            ngx.log(ngx.NOTICE, \"get val: \", val)\n        end\n    }\n--- config\n    location /t {\n        content_by_lua_block {\n            local dog = ngx.shared.dog\n            dog:set(\"foo\", 100)\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- shutdown_error_log\nget val: 100\n\n\n\n=== TEST 6: skip in cache processes (with exit worker and privileged agent)\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;\";\n\n    proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:1m;\n\n    init_by_lua_block {\n        assert(require \"ngx.process\".enable_privileged_agent())\n    }\n\n    exit_worker_by_lua_block {\n        local process = require \"ngx.process\"\n        ngx.log(ngx.INFO, \"hello from exit worker by lua, process type: \", process.type())\n    }\n--- config\n    location = /t {\n        return 200;\n    }\n--- request\n    GET /t\n--- no_error_log\n[error]\n--- shutdown_error_log eval\n[\nqr/cache loader process \\d+ exited/,\nqr/cache manager process \\d+ exited/,\nqr/hello from exit worker by lua, process type: worker/,\nqr/hello from exit worker by lua, process type: privileged agent/,\nqr/privileged agent process \\d+ exited/,\n]\n\n\n\n=== TEST 7: skipin cache processes (with init worker but without privileged agent)\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;\";\n\n    proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:1m;\n\n    exit_worker_by_lua_block {\n        local process = require \"ngx.process\"\n        ngx.log(ngx.INFO, \"hello from exit worker by lua, process type: \", process.type())\n    }\n--- config\n    location = /t {\n        return 200;\n    }\n--- request\n    GET /t\n--- no_error_log\n[error]\nstart privileged agent process\n--- shutdown_error_log eval\n[\nqr/cache loader process \\d+ exited/,\nqr/cache manager process \\d+ exited/,\nqr/hello from exit worker by lua, process type: worker/,\n]\n\n\n\n=== TEST 8: syntax error in exit_worker_by_lua_block\n--- http_config\n    exit_worker_by_lua_block {\n        ngx.log(ngx.debug, \"pass\")\n        error(\"failed to init\"\n        ngx.log(ngx.debug, \"unreachable\")\n    }\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.say(\"hello world\")\n        }\n    }\n--- request\n    GET /t\n--- response_body\nhello world\n--- shutdown_error_log\nexit_worker_by_lua error: exit_worker_by_lua(nginx.conf:25):4: ')' expected (to close '(' at line 3) near 'ngx'\n\n\n\n=== TEST 9: syntax error in exit_worker_by_lua_file\n--- http_config\n    exit_worker_by_lua_file html/exit.lua;\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.say(\"hello world\")\n        }\n    }\n--- user_files\n>>> exit.lua\n    ngx.log(ngx.debug, \"pass\")\n    error(\"failed to init\"\n    ngx.log(ngx.debug, \"unreachable\")\n\n--- request\n    GET /t\n--- response_body\nhello world\n--- shutdown_error_log eval\nqr|exit_worker_by_lua_file error: .*?t/servroot\\w*/html/exit.lua:3: '\\)' expected \\(to close '\\(' at line 2\\) near 'ngx'|\n"
  },
  {
    "path": "t/162-socket-tls-handshake.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 43;\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\n\nsub read_file {\n    my $infile = shift;\n    open my $in, $infile\n        or die \"cannot open $infile for reading: $!\";\n    my $cert = do { local $/; <$in> };\n    close $in;\n    $cert;\n}\n\nour $MTLSCA = read_file(\"t/cert/mtls_ca.crt\");\nour $MTLSClient = read_file(\"t/cert/mtls_client.crt\");\nour $MTLSClientKey = read_file(\"t/cert/mtls_client.key\");\nour $MTLSServer = read_file(\"t/cert/mtls_server.crt\");\nour $MTLSServerKey = read_file(\"t/cert/mtls_server.key\");\n\nour $HtmlDir = html_dir;\n\nour $mtls_http_config = <<\"_EOC_\";\nserver {\n    listen unix:$::HtmlDir/mtls.sock ssl;\n\n    ssl_certificate        $::HtmlDir/mtls_server.crt;\n    ssl_certificate_key    $::HtmlDir/mtls_server.key;\n    ssl_client_certificate $::HtmlDir/mtls_ca.crt;\n    ssl_verify_client      on;\n    server_tokens          off;\n\n    location / {\n        return 200 \"hello, \\$ssl_client_s_dn\";\n    }\n}\n_EOC_\n\nour $mtls_user_files = <<\"_EOC_\";\n>>> mtls_server.key\n$::MTLSServerKey\n>>> mtls_server.crt\n$::MTLSServer\n>>> mtls_ca.crt\n$::MTLSCA\n>>> mtls_client.key\n$::MTLSClientKey\n>>> mtls_client.crt\n$::MTLSClient\n_EOC_\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity: www.google.com\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n\n    location /t {\n        content_by_lua_block {\n            -- avoid flushing bing in \"check leak\" testing mode:\n            local counter = package.loaded.counter\n            if not counter then\n                counter = 1\n            elseif counter >= 2 then\n                return ngx.exit(503)\n            else\n                counter = counter + 1\n            end\n\n            package.loaded.counter = counter\n\n            do\n                local sock = ngx.socket.tcp()\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"www.google.com\", 443)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake()\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET / HTTP/1.1\\r\\nHost: www.google.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n\n            collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body_like chop\n\\Aconnected: 1\nssl handshake: cdata\nsent http request: 59 bytes.\nreceived: HTTP/1.1 (?:200 OK|302 Found)\nclose: 1 nil\n\\z\n--- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/\n--- grep_error_log_out eval\nqr/^lua ssl save session: ([0-9A-F]+)\nlua ssl free session: ([0-9A-F]+)\n$/\n--- no_error_log\nlua ssl server name:\nSSL reused session\n[error]\n[alert]\n--- timeout: 5\n\n\n\n=== TEST 2: mutual TLS handshake, upstream is not accessible without client certs\n--- http_config eval: $::mtls_http_config\n--- config eval\n\"\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect('unix:$::HtmlDir/mtls.sock')\n            if not ok then\n                ngx.say('failed to connect: ', err)\n            end\n\n            assert(sock:sslhandshake())\n\n            ngx.say('connected: ', ok)\n\n            local req = 'GET /\\\\r\\\\n'\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say('failed to send request: ', err)\n                return\n            end\n\n            ngx.say('request sent: ', bytes)\n\n            ngx.say(sock:receive('*a'))\n\n            assert(sock:close())\n        }\n    }\n\"\n--- user_files eval: $::mtls_user_files\n--- request\nGET /t\n--- response_body_like: 400 No required SSL certificate was sent\n--- no_error_log\n[alert]\n[error]\n[crit]\n[emerg]\n\n\n\n=== TEST 3: mutual TLS handshake, upstream is accessible with client certs\n--- http_config eval: $::mtls_http_config\n--- config eval\n\"\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect('unix:$::HtmlDir/mtls.sock')\n            if not ok then\n                ngx.say('failed to connect: ', err)\n            end\n\n            local f = assert(io.open('$::HtmlDir/mtls_client.crt'))\n            local cert_data = f:read('*a')\n            f:close()\n\n            f = assert(io.open('$::HtmlDir/mtls_client.key'))\n            local key_data = f:read('*a')\n            f:close()\n\n            local ssl = require('ngx.ssl')\n\n            local chain = assert(ssl.parse_pem_cert(cert_data))\n            local priv = assert(ssl.parse_pem_priv_key(key_data))\n\n            sock:setclientcert(chain, priv)\n\n            assert(sock:sslhandshake())\n\n            ngx.say('connected: ', ok)\n\n            local req = 'GET /\\\\r\\\\n'\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say('failed to send request: ', err)\n                return\n            end\n\n            ngx.say('request sent: ', bytes)\n\n            ngx.say(sock:receive('*a'))\n\n            assert(sock:close())\n        }\n    }\n\"\n--- user_files eval: $::mtls_user_files\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent: 7\nhello, CN=foo@example.com,O=OpenResty,ST=California,C=US\n--- no_error_log\n[alert]\n[error]\n[crit]\n[emerg]\n\n\n\n=== TEST 4: incorrect type of client cert\n--- config\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:setclientcert(\"doesnt\", \"work\")\n            if not ok then\n                ngx.say('failed to setclientcert: ', err)\n                return\n            end\n\n            assert(sock:close())\n        }\n    }\n--- request\nGET /t\n--- response_body\nfailed to setclientcert: bad cert arg: cdata expected, got string\n--- no_error_log\n[alert]\n[error]\n[crit]\n[emerg]\n\n\n\n=== TEST 5: incorrect type of client key\n--- config eval\n\"\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            local f = assert(io.open('$::HtmlDir/mtls_client.crt'))\n            local cert_data = f:read('*a')\n            f:close()\n\n            local ssl = require('ngx.ssl')\n\n            local chain = assert(ssl.parse_pem_cert(cert_data))\n\n            local ok, err = sock:setclientcert(chain, 'work')\n            if not ok then\n                ngx.say('failed to setclientcert: ', err)\n                return\n            end\n\n            assert(sock:close())\n        }\n    }\n\"\n--- user_files eval: $::mtls_user_files\n--- request\nGET /t\n--- response_body\nfailed to setclientcert: bad pkey arg: cdata expected, got string\n--- no_error_log\n[alert]\n[error]\n[crit]\n[emerg]\n\n\n\n=== TEST 6: missing client cert\n--- config\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:setclientcert(nil, \"work\")\n            if not ok then\n                ngx.say('failed to setclientcert: ', err)\n                return\n            end\n\n            assert(sock:close())\n        }\n    }\n--- request\nGET /t\n--- response_body\nfailed to setclientcert: client certificate must be supplied with corresponding private key\n--- no_error_log\n[alert]\n[error]\n[crit]\n[emerg]\n\n\n\n=== TEST 7: missing private key\n--- config\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:setclientcert('doesnt', nil)\n            if not ok then\n                ngx.say('failed to setclientcert: ', err)\n                return\n            end\n\n            assert(sock:close())\n        }\n    }\n--- request\nGET /t\n--- response_body\nfailed to setclientcert: client certificate must be supplied with corresponding private key\n--- no_error_log\n[alert]\n[error]\n[crit]\n[emerg]\n"
  },
  {
    "path": "t/162-static-module-location.t",
    "content": "use Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3);\n\nour $HtmlDir = html_dir;\n\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: decoded url contains '\\0' and '\\r\\n'\n--- config\n    server_tokens off;\n    location = /t {\n        rewrite_by_lua_block {\n            ngx.req.read_body();\n            local args, _ = ngx.req.get_post_args();\n            ngx.req.set_uri(args[\"url\"], true, true);\n        }\n    }\n--- request\nPOST /t\nurl=%00%0a%0dset-cookie:1234567\n--- error_code: 301\n--- response_headers\nLocation: %00%0A%0Dset-cookie:1234567/\n--- response_body_like\n.*301 Moved Permanently.*\n\n\n\n=== TEST 2: uri contain chinese characters\n--- config\n    server_tokens off;\n--- user_files\n>>> t/中文/foo.txt\nHello, world\n--- request\nGET /t/中文\n--- more_headers\nhost: localhost\n--- error_code: 301\n--- response_headers_like\nLocation: https?:\\/\\/localhost:\\d+\\/t\\/%E4%B8%AD%E6%96%87\\/\n--- response_body_like\n.*301 Moved Permanently.*\n\n\n\n=== TEST 3: uri contain chinese characters with args\n--- config\n    server_tokens off;\n--- user_files\n>>> t/中文/foo.txt\nHello, world\n--- request\nGET /t/中文?q=name\n--- more_headers\nhost: localhost\n--- error_code: 301\n--- response_headers_like\nLocation: https?:\\/\\/localhost:\\d+\\/t\\/%E4%B8%AD%E6%96%87\\/\\?q=name\n--- response_body_like\n.*301 Moved Permanently.*\n\n\n\n=== TEST 4: uri already encoded\n--- config\n    server_tokens off;\n--- user_files\n>>> t/中文/foo.txt\nHello, world\n--- request\nGET /t/%E4%B8%AD%E6%96%87\n--- more_headers\nhost: localhost\n--- error_code: 301\n--- response_headers_like\nLocation: https?:\\/\\/localhost:\\d+\\/t\\/%E4%B8%AD%E6%96%87\\/\n--- response_body_like\n.*301 Moved Permanently.*\n\n\n\n=== TEST 5: uri already encoded with args\n--- config\n    server_tokens off;\n--- user_files\n>>> t/中文/foo.txt\nHello, world\n--- request\nGET /t/%E4%B8%AD%E6%96%87?q=name\n--- more_headers\nhost: localhost\n--- error_code: 301\n--- response_headers_like\nLocation: https?://localhost:\\d+\\/t\\/%E4%B8%AD%E6%96%87\\/\\?q=name\n--- response_body_like\n.*301 Moved Permanently.*\n"
  },
  {
    "path": "t/163-exit-worker-hup.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_CHECK_LEAK}) {\n        $SkipReason = \"unavailable for the hup tests\";\n\n    } elsif ($ENV{TEST_NGINX_USE_HTTP3}) {\n        $SkipReason = \"http3 does not support hub reload\";\n\n    } else {\n        $ENV{TEST_NGINX_USE_HUP} = 1;\n        undef $ENV{TEST_NGINX_USE_STAP};\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 2 + 1) + 2;\n\nno_long_string();\n\nworker_connections(1024);\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple exit_worker_by_lua_block with hup\n--- http_config\n    exit_worker_by_lua_block {\n        ngx.log(ngx.NOTICE, \"log from exit_worker_by_lua_block\")\n    }\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- shutdown_error_log\nlog from exit_worker_by_lua_block\n\n\n\n=== TEST 2: exit after worker_shutdown_timeout\n--- main_config\n    worker_shutdown_timeout 1;\n--- http_config\n    exit_worker_by_lua_block {\n        ngx.log(ngx.NOTICE, \"log from exit_worker_by_lua_block\")\n    }\n\n    server {\n        listen $TEST_NGINX_RAND_PORT_1;\n\n        location = /t {\n            echo 'hello world';\n        }\n    }\n--- config\n    location /t {\n        content_by_lua_block {\n            ngx.timer.at(0, function ()\n                local sock = ngx.socket.tcp()\n                sock:connect(\"127.0.0.1\", $TEST_NGINX_RAND_PORT_1)\n                local reader = sock:receiveuntil(\"unknow\")\n                ngx.log(ngx.NOTICE, \"reading to block the exiting\")\n                reader()\n            end)\n\n            ngx.sleep(0)\n\n            ngx.say(\"ok\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nok\n--- error_log\nreading to block the exiting\n--- shutdown_error_log\nlog from exit_worker_by_lua_block\n"
  },
  {
    "path": "t/163-signal.t",
    "content": "# vi:ft=\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_USE_HUP}) {\n        $SkipReason = \"unavailable under hup test mode\";\n\n    } elsif ($ENV{TEST_NGINX_CHECK_LEAK}) {\n        $SkipReason = \"unavailable under check leak test mode\";\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\nplan tests => 2 * blocks();\n\nno_long_string();\n\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: SIGHUP followed by SIGQUIT\n--- config\n    location = /t {\n        content_by_lua_block {\n            local pid = ngx.worker.pid()\n            os.execute(\"kill -HUP \" .. pid)\n            ngx.sleep(0.01)\n\n            os.execute(\"kill -QUIT \" .. pid)\n        }\n    }\n--- request\nGET /t\n--- ignore_response\n--- wait: 0.1\n--- error_log eval\nqr/\\[notice\\] \\d+#\\d+: exit$/\n--- no_error_log eval\nqr/\\[notice\\] \\d+#\\d+: reconfiguring/\n--- curl_error eval\nqr/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\\)/\n\n\n\n=== TEST 2: exit after receiving SIGHUP in single process mode\n--- config\n    location = /t {\n        content_by_lua_block {\n            local pid = ngx.worker.pid()\n            os.execute(\"kill -HUP \" .. pid)\n        }\n    }\n--- request\nGET /t\n--- ignore_response\n--- wait: 0.1\n--- error_log eval\nqr/\\[notice\\] \\d+#\\d+: exit$/\n--- no_error_log eval\nqr/\\[notice\\] \\d+#\\d+: reconfiguring/\n"
  },
  {
    "path": "t/164-say.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nplan tests => blocks() * repeat_each() * 2;\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: ngx.say (integer)\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(2)\n        }\n    }\n--- request\nGET /lua\n--- response_body\n2\n\n\n\n=== TEST 2: ngx.say (floating point number)\nthe maximum number of significant digits is 14 in lua\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(3.1415926)\n            ngx.say(3.14159265357939723846)\n        }\n    }\n--- request\nGET /lua\n--- response_body\n3.1415926\n3.1415926535794\n\n\n\n=== TEST 3: ngx.say (table with number)\n--- config\n    location /lua {\n        content_by_lua_block {\n            local data = {123,\" \", 3.1415926}\n            ngx.say(data)\n        }\n    }\n--- request\nGET /lua\n--- response_body\n123 3.1415926\n\n\n\n=== TEST 4: ngx.say min int32 -2147483648\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(-2147483648)\n        }\n    }\n--- request\nGET /lua\n--- response_body\n-2147483648\n\n\n\n=== TEST 5: ngx.say big integer 2147483647\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(2147483647)\n        }\n    }\n--- request\nGET /lua\n--- response_body\n2147483647\n\n\n\n=== TEST 6: ngx.say big integer -9223372036854775808\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(-9223372036854775808)\n        }\n    }\n--- request\nGET /lua\n--- response_body\n-9.2233720368548e+18\n\n\n\n=== TEST 7: ngx.say big integer 18446744073709551615\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(18446744073709551615)\n        }\n    }\n--- request\nGET /lua\n--- response_body\n1.844674407371e+19\n"
  },
  {
    "path": "t/165-thread-cache.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 4);\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: thread cache size == 1\n--- http_config\n    lua_thread_cache_max_entries 1;\n\n--- config\n    location /lua {\n        # NOTE: the newline escape sequence must be double-escaped, as nginx config\n        # parser will unescape first!\n        content_by_lua '\n            local ok, err = ngx.print(\"Hello, Lua!\\\\n\")\n            if not ok then\n                ngx.log(ngx.ERR, \"print failed: \", err)\n            end\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nHello, Lua!\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/lua caching unused lua thread|lua reusing cached lua thread/\n--- grep_error_log_out eval\n[\n    \"lua caching unused lua thread\\n\",\n    \"lua reusing cached lua thread\nlua caching unused lua thread\n\",\n]\n\n\n\n=== TEST 2: thread cache size == 0\n--- http_config\n    lua_thread_cache_max_entries 0;\n\n--- config\n    location /lua {\n        # NOTE: the newline escape sequence must be double-escaped, as nginx config\n        # parser will unescape first!\n        content_by_lua '\n            local ok, err = ngx.print(\"Hello, Lua!\\\\n\")\n            if not ok then\n                ngx.log(ngx.ERR, \"print failed: \", err)\n            end\n        ';\n    }\n--- request\nGET /lua\n--- response_body\nHello, Lua!\n--- no_error_log\n[error]\n--- grep_error_log eval: qr/lua caching unused lua thread|lua reusing cached lua thread/\n--- grep_error_log_out eval\n[\n    \"\",\n    \"\",\n]\n"
  },
  {
    "path": "t/166-ssl-client-hello.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(3);\n\n# All these tests need to have new openssl\nmy $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';\nmy $openssl_version = eval { `$NginxBinary -V 2>&1` };\n\nif ($openssl_version =~ m/built with OpenSSL (0\\S*|1\\.0\\S*|1\\.1\\.0\\S*)/) {\n    plan(skip_all => \"too old OpenSSL, need >= 1.1.1, was $1\");\n} elsif ($openssl_version =~ m/running with BoringSSL/) {\n    plan(skip_all => \"does not support BoringSSL\");\n} elsif ($ENV{TEST_NGINX_USE_HTTP3}) {\n    plan tests => repeat_each() * (blocks() * 6 + 6);\n} else {\n    plan tests => repeat_each() * (blocks() * 6 + 10);\n}\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_QUIC_IDLE_TIMEOUT} ||= 0.6;\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple logging\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_client_hello_by_lua_block { print(\"ssl client hello by lua is running!\") }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\n\n--- no_error_log\n[error]\n[alert]\n--- grep_error_log eval: qr/ssl_client_hello_by_lua\\(.*?,|\\bssl client hello: connection reusable: \\d+|\\breusable connection: \\d+/\n--- grep_error_log_out eval\n# Since nginx version 1.17.9, nginx call ngx_reusable_connection(c, 0)\n# before call ssl callback function\n$Test::Nginx::Util::NginxVersion >= 1.017009 ?\nqr/reusable connection: 0\nssl client hello: connection reusable: 0\nssl_client_hello_by_lua\\(nginx.conf:\\d+\\):1: ssl client hello by lua is running!,/\n: qr /reusable connection: 1\nssl client hello: connection reusable: 1\nreusable connection: 0\nssl_client_hello_by_lua\\(nginx.conf:\\d+\\):1: ssl client hello by lua is running!,/\n\n\n\n=== TEST 2: sleep\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_client_hello_by_lua_block {\n            local begin = ngx.now()\n            ngx.sleep(0.1)\n            print(\"elapsed in ssl client hello by lua: \", ngx.now() - begin)\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log eval\n[\n'lua ssl server name: \"test.com\"',\nqr/elapsed in ssl client hello by lua: 0.(?:09|1\\d)\\d+,/,\n]\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 3: timer\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_client_hello_by_lua_block {\n            local function f()\n                print(\"my timer run!\")\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to create timer: \", err)\n                return\n            end\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nmy timer run!\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 4: cosocket\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_client_hello_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            sock:settimeout(2000)\n\n            local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to connect to memc: \", err)\n                return\n            end\n\n            local bytes, err = sock:send(\"flush_all\\r\\n\")\n            if not bytes then\n                ngx.log(ngx.ERR, \"failed to send flush_all command: \", err)\n                return\n            end\n\n            local res, err = sock:receive()\n            if not res then\n                ngx.log(ngx.ERR, \"failed to receive memc reply: \", err)\n                return\n            end\n\n            print(\"received memc reply: \", res)\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nreceived memc reply: OK\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 5: ngx.exit(0) - no yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_client_hello_by_lua_block {\n            ngx.exit(0)\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: boolean\n\n--- error_log\nlua exit with code 0\n\n--- no_error_log\nshould never reached here\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 6: ngx.exit(ngx.ERROR) - no yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_client_hello_by_lua_block {\n            ngx.exit(ngx.ERROR)\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- error_log eval\n[\n'ssl_client_hello_by_lua: handler return value: -1, client hello cb exit code: 0',\nqr/\\[info\\] .*? SSL_do_handshake\\(\\) failed .*?callback failed/,\n'lua exit with code -1',\n]\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 7: ngx.exit(0) -  yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_client_hello_by_lua_block {\n            ngx.sleep(0.001)\n            ngx.exit(0)\n\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: boolean\n\n--- error_log\nlua exit with code 0\n\n--- no_error_log\nshould never reached here\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 8: ngx.exit(ngx.ERROR) - yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_client_hello_by_lua_block {\n            ngx.sleep(0.001)\n            ngx.exit(ngx.ERROR)\n\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- error_log eval\n[\n'ssl_client_hello_by_lua: client hello cb exit code: 0',\nqr/\\[info\\] .*? SSL_do_handshake\\(\\) failed .*?callback failed/,\n'lua exit with code -1',\n]\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 9: lua exception - no yield\n--- http_config\n    server {\n        listen 127.0.0.2:$TEST_NGINX_RAND_PORT_2 ssl;\n        server_name test.com;\n        ssl_client_hello_by_lua_block {\n            error(\"bad bad bad\")\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"127.0.0.2\", $TEST_NGINX_RAND_PORT_2)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- error_log eval\n[\n'runtime error: ssl_client_hello_by_lua(nginx.conf:28):2: bad bad bad',\n'ssl_client_hello_by_lua: handler return value: 500, client hello cb exit code: 0',\nqr/\\[info\\] .*? SSL_do_handshake\\(\\) failed .*?callback failed/,\nqr/context: ssl_client_hello_by_lua\\*, client: \\d+\\.\\d+\\.\\d+\\.\\d+, server: \\d+\\.\\d+\\.\\d+\\.\\d+:\\d+/,\n]\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 10: lua exception - yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_client_hello_by_lua_block {\n            ngx.sleep(0.001)\n            error(\"bad bad bad\")\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- error_log eval\n[\n'runtime error: ssl_client_hello_by_lua(nginx.conf:28):3: bad bad bad',\n'ssl_client_hello_by_lua: client hello cb exit code: 0',\nqr/\\[info\\] .*? SSL_do_handshake\\(\\) failed .*?callback failed/,\n]\n\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 11: get phase\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_client_hello_by_lua_block {print(\"get_phase: \", ngx.get_phase())}\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end\n            collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\n\n--- error_log\nlua ssl server name: \"test.com\"\nget_phase: ssl_client_hello\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 12: connection aborted prematurely\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_client_hello_by_lua_block {\n            ngx.sleep(0.3)\n            print(\"ssl-client-hello-by-lua: after sleeping\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(150)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n\n--- response_body\nconnected: 1\nfailed to do SSL handshake: timeout\n\n--- error_log\nlua ssl server name: \"test.com\"\nssl-client-hello-by-lua: after sleeping\n\n--- no_error_log\n[error]\n[alert]\n--- wait: 0.6\n\n\n\n=== TEST 13: subrequests disabled\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_client_hello_by_lua_block {ngx.location.capture(\"/foo\")}\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to do SSL handshake: handshake failed\n\n--- error_log eval\n[\n'lua ssl server name: \"test.com\"',\n'ssl_client_hello_by_lua(nginx.conf:28):1: API disabled in the context of ssl_client_hello_by_lua*',\nqr/\\[info\\] .*?callback failed/,\n]\n\n--- no_error_log\n[alert]\n\n\n\n=== TEST 14: simple logging (by_lua_file)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_client_hello_by_lua_file html/a.lua;\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n\n--- user_files\n>>> a.lua\nprint(\"ssl client hello by lua is running!\")\n\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\na.lua:1: ssl client hello by lua is running!\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 15: coroutine API\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_client_hello_by_lua_block {\n            local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n\n            local function f()\n                local cnt = 0\n                for i = 1, 20 do\n                    print(\"co yield: \", cnt)\n                    cy()\n                    cnt = cnt + 1\n                end\n            end\n\n            local c = cc(f)\n            for i = 1, 3 do\n                print(\"co resume, status: \", coroutine.status(c))\n                cr(c)\n            end\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- grep_error_log eval: qr/co (?:yield: \\d+|resume, status: \\w+)/\n--- grep_error_log_out\nco resume, status: suspended\nco yield: 0\nco resume, status: suspended\nco yield: 1\nco resume, status: suspended\nco yield: 2\n\n--- error_log\nlua ssl server name: \"test.com\"\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 16: simple user thread wait with yielding\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        ssl_client_hello_by_lua_block {\n            local function f()\n                ngx.sleep(0.01)\n                print(\"uthread: hello in thread\")\n                return \"done\"\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.log(ngx.ERR, \"uthread: failed to spawn thread: \", err)\n                return ngx.exit(ngx.ERROR)\n            end\n\n            print(\"uthread: thread created: \", coroutine.status(t))\n\n            local ok, res = ngx.thread.wait(t)\n            if not ok then\n                print(\"uthread: failed to wait thread: \", res)\n                return\n            end\n\n            print(\"uthread: \", res)\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- no_error_log\n[error]\n[alert]\n--- grep_error_log eval: qr/uthread: [^.,]+/\n--- grep_error_log_out\nuthread: thread created: running\nuthread: hello in thread\nuthread: done\n\n\n\n=== TEST 17: simple logging - use ssl_client_hello_by_lua* on the http {} level\nGitHub openresty/lua-resty-core#42\n--- http_config\n    ssl_client_hello_by_lua_block { print(\"ssl client hello by lua is running!\") }\n    ssl_certificate ../../cert/test.crt;\n    ssl_certificate_key ../../cert/test.key;\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nssl_client_hello_by_lua(nginx.conf:25):1: ssl client hello by lua is running!\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 18: simple logging - use ssl_client_hello_by_lua* on the http {} level and server {} level\n--- http_config\n    ssl_client_hello_by_lua_block { print(\"ssl client hello by lua on the http level is running!\") }\n    ssl_certificate ../../cert/test.crt;\n    ssl_certificate_key ../../cert/test.key;\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        ssl_client_hello_by_lua_block { print(\"ssl client hello by lua on the server level is running!\") }\n        server_name   test.com;\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nssl_client_hello_by_lua(nginx.conf:31):1: ssl client hello by lua on the server level is running!\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 19: use ssl_client_hello_by_lua* on the server {} level with non-ssl server\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        ssl_client_hello_by_lua_block { print(\"ssl client hello by lua is running!\") }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        server_name   test.com;\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- no_error_log\nssl client hello by lua is running!\n[error]\n[alert]\n\n\n\n=== TEST 20: use ssl_client_hello_by_lua* on the http {} level with non-ssl server\n--- http_config\n    ssl_certificate ../../cert/test.crt;\n    ssl_certificate_key ../../cert/test.key;\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        ssl_client_hello_by_lua_block { print(\"ssl client hello by lua is running!\") }\n        server_name   test.com;\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- no_error_log\nssl client hello by lua is running!\n[error]\n[alert]\n\n\n\n=== TEST 21: listen two ports (one for ssl and one for non-ssl) in one server - connect ssl port\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        listen unix:$TEST_NGINX_HTML_DIR/nginx2.sock;\n        ssl_client_hello_by_lua_block { print(\"ssl client hello by lua is running!\") }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        server_name   test.com;\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nssl_client_hello_by_lua(nginx.conf:28):1: ssl client hello by lua is running!\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 22: listen two ports (one for ssl and one for non-ssl) in one server - connect non-ssl port\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        listen unix:$TEST_NGINX_HTML_DIR/nginx2.sock;\n        ssl_client_hello_by_lua_block { print(\"ssl client hello by lua is running!\") }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        server_name   test.com;\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx2.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n\n--- no_error_log\nssl client hello by lua is running!\n[error]\n[alert]\n\n\n\n=== TEST 23: simple logging - use ssl_client_hello_by_lua* in multiple virtual servers\n--- http_config\n    ssl_certificate ../../cert/test.crt;\n    ssl_certificate_key ../../cert/test.key;\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        ssl_client_hello_by_lua_block { print(\"ssl client hello by lua in server1 is running!\") }\n        server_name   test.com;\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        ssl_client_hello_by_lua_block { print(\"ssl client hello by lua in server2 is running!\") }\n        server_name   test2.com;\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo2\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test2.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 57 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 5\nreceived: Connection: close\nreceived: \nreceived: foo2\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nssl_client_hello_by_lua(nginx.conf:29):1: ssl client hello by lua in server1 is running!\n\n--- no_error_log\nssl client hello by lua in server2 is running!\n[error]\n[alert]\n\n\n\n=== TEST 24: simple logging (syslog)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        error_log syslog:server=127.0.0.1:12345 debug;\n\n        ssl_client_hello_by_lua_block { print(\"ssl client hello by lua is running!\") }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log eval\n[\nqr/\\[error\\] .*? send\\(\\) failed/,\n'lua ssl server name: \"test.com\"',\n]\n--- no_error_log\n[alert]\nssl client hello by lua is running!\n\n\n\n=== TEST 25: get raw_client_addr - IPv4\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;;\";\n\n    server {\n        listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1 ssl;\n        server_name   test.com;\n\n        ssl_client_hello_by_lua_block {\n            local ssl = require \"ngx.ssl\"\n            local byte = string.byte\n            local addr, addrtype, err = ssl.raw_client_addr()\n            local ip = string.format(\"%d.%d.%d.%d\", byte(addr, 1), byte(addr, 2),\n                       byte(addr, 3), byte(addr, 4))\n            print(\"client ip: \", ip)\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_RAND_PORT_1)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nclient ip: 127.0.0.1\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 26: get raw_client_addr - unix domain socket\n--- http_config\n    lua_package_path \"../lua-resty-core/lib/?.lua;;\";\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_client_hello_by_lua_block {\n            local ssl = require \"ngx.ssl\"\n            local addr, addrtyp, err = ssl.raw_client_addr()\n            print(\"client socket file: \", addr)\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nclient socket file:\n\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 27: ssl_client_hello_by_lua* can yield when reading early data\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        ssl_early_data on;\n        server_tokens off;\n\n        ssl_client_hello_by_lua_block {\n            local begin = ngx.now()\n            ngx.sleep(0.1)\n            print(\"elapsed in ssl_client_hello_by_lua*: \", ngx.now() - begin)\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: boolean\n--- grep_error_log eval\nqr/elapsed in ssl_client_hello_by_lua\\*: 0\\.(?:09|1\\d)\\d+,/,\n--- grep_error_log_out eval\n[\nqr/elapsed in ssl_client_hello_by_lua\\*: 0\\.(?:09|1\\d)\\d+,/,\nqr/elapsed in ssl_client_hello_by_lua\\*: 0\\.(?:09|1\\d)\\d+,/,\nqr/elapsed in ssl_client_hello_by_lua\\*: 0\\.(?:09|1\\d)\\d+,/,\n]\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 28: cosocket (UDP)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        server_tokens off;\n\n        ssl_client_hello_by_lua_block {\n            local sock = ngx.socket.udp()\n\n            sock:settimeout(1000)\n\n            local ok, err = sock:setpeername(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to connect to memc: \", err)\n                return\n            end\n\n            local req = \"\\0\\1\\0\\0\\0\\1\\0\\0flush_all\\r\\n\"\n            local ok, err = sock:send(req)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to send flush_all to memc: \", err)\n                return\n            end\n\n            local res, err = sock:receive()\n            if not res then\n                ngx.log(ngx.ERR, \"failed to receive memc reply: \", err)\n                return\n            end\n\n            ngx.log(ngx.INFO, \"received memc reply of \", #res, \" bytes\")\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\n--- no_error_log\n[error]\n[alert]\n[emerg]\n--- grep_error_log eval: qr/received memc reply of \\d+ bytes/\n--- grep_error_log_out eval\n[\n'received memc reply of 12 bytes\n',\n'received memc reply of 12 bytes\n',\n'received memc reply of 12 bytes\n',\n'received memc reply of 12 bytes\n',\n]\n\n\n\n=== TEST 29: uthread (kill)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        server_tokens off;\n\n        ssl_client_hello_by_lua_block {\n            local function f()\n                ngx.log(ngx.INFO, \"uthread: hello from f()\")\n                ngx.sleep(1)\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.log(ngx.ERR, \"failed to spawn thread: \", err)\n                return ngx.exit(ngx.ERROR)\n            end\n\n            local ok, res = ngx.thread.kill(t)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to kill thread: \", res)\n                return\n            end\n\n            ngx.log(ngx.INFO, \"uthread: killed\")\n\n            local ok, err = ngx.thread.kill(t)\n            if not ok then\n                ngx.log(ngx.INFO, \"uthread: failed to kill: \", err)\n            end\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\n--- no_error_log\n[error]\n[alert]\n[emerg]\n--- grep_error_log eval: qr/uthread: [^.,]+/\n--- grep_error_log_out\nuthread: hello from f()\nuthread: killed\nuthread: failed to kill: already waited or killed\n\n\n\n=== TEST 30: ngx.exit(ngx.OK) - no yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name test.com;\n        ssl_client_hello_by_lua_block {\n            ngx.exit(ngx.OK)\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {ngx.status = 201 ngx.say(\"foo\") ngx.exit(201)}\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(false, nil, true, false)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n            end  -- do\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: boolean\n\n--- error_log eval\n[\n'ssl_client_hello_by_lua: handler return value: 0, client hello cb exit code: 1',\nqr/\\[debug\\] .*? SSL_do_handshake: 1/,\n'lua exit with code 0',\n]\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 31: ssl_client_hello_by_lua* can yield when reading early data\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_client_hello_by_lua_block {\n        print(\"ssl client hello by lua is running!\")\n        ngx.sleep(0.1)\n        print(\"ssl client hello by lua is done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- grep_error_log eval: qr/(ssl client hello by lua is running!|ssl client hello by lua is done|test completed)/\n--- grep_error_log_out\nssl client hello by lua is running!\nssl client hello by lua is done\ntest completed\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 32: ssl_client_hello_by_lua* with TCP cosocket (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_client_hello_by_lua_block {\n        print(\"ssl client hello: TCP cosocket test start\")\n\n        local sock = ngx.socket.tcp()\n        sock:settimeout(2000)\n\n        local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to connect to memc: \", err)\n            return\n        end\n\n        local bytes, err = sock:send(\"flush_all\\r\\n\")\n        if not bytes then\n            ngx.log(ngx.ERR, \"failed to send flush_all command: \", err)\n            return\n        end\n\n        local res, err = sock:receive()\n        if not res then\n            ngx.log(ngx.ERR, \"failed to receive memc reply: \", err)\n            return\n        end\n\n        print(\"ssl client hello: received TCP memc reply: \", res)\n        sock:close()\n        print(\"ssl client hello: TCP cosocket test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out\nssl client hello: TCP cosocket test start\nssl client hello: received TCP memc reply: OK\nssl client hello: TCP cosocket test done\ntest completed\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 33: ssl_client_hello_by_lua* with UDP cosocket (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_client_hello_by_lua_block {\n        print(\"ssl client hello: UDP cosocket test start\")\n\n        local sock = ngx.socket.udp()\n        sock:settimeout(1000)\n\n        local ok, err = sock:setpeername(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to connect to memc: \", err)\n            return\n        end\n\n        local req = \"\\0\\1\\0\\0\\0\\1\\0\\0flush_all\\r\\n\"\n        local ok, err = sock:send(req)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to send flush_all to memc: \", err)\n            return\n        end\n\n        local res, err = sock:receive()\n        if not res then\n            ngx.log(ngx.ERR, \"failed to receive memc reply: \", err)\n            return\n        end\n\n        print(\"ssl client hello: received UDP memc reply of \", #res, \" bytes\")\n        sock:close()\n        print(\"ssl client hello: UDP cosocket test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out\nssl client hello: UDP cosocket test start\nssl client hello: received UDP memc reply of 12 bytes\nssl client hello: UDP cosocket test done\ntest completed\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 34: ssl_client_hello_by_lua* with ngx.timer (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_client_hello_by_lua_block {\n        print(\"ssl client hello: timer test start\")\n\n        local function timer_handler()\n            print(\"ssl client hello: timer executed\")\n        end\n\n        local ok, err = ngx.timer.at(0, timer_handler)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to create timer: \", err)\n            return\n        end\n\n        print(\"ssl client hello: timer created\")\n        print(\"ssl client hello: timer test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out\nssl client hello: timer test start\nssl client hello: timer created\nssl client hello: timer test done\nssl client hello: timer executed\ntest completed\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 35: ssl_client_hello_by_lua* with user threads (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_client_hello_by_lua_block {\n        print(\"ssl client hello: uthread test start\")\n\n        local function worker()\n            ngx.sleep(0.01)\n            print(\"ssl client hello: uthread worker executed\")\n            return \"worker_result\"\n        end\n\n        local t, err = ngx.thread.spawn(worker)\n        if not t then\n            ngx.log(ngx.ERR, \"failed to spawn thread: \", err)\n            return\n        end\n\n        print(\"ssl client hello: uthread spawned\")\n\n        local ok, res = ngx.thread.wait(t)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to wait thread: \", res)\n            return\n        end\n\n        print(\"ssl client hello: uthread result: \", res)\n        print(\"ssl client hello: uthread test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out\nssl client hello: uthread test start\nssl client hello: uthread spawned\nssl client hello: uthread worker executed\nssl client hello: uthread result: worker_result\nssl client hello: uthread test done\ntest completed\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 36: ssl_client_hello_by_lua* with coroutines (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_client_hello_by_lua_block {\n        print(\"ssl client hello: coroutine test start\")\n\n        local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n\n        local function coro_func()\n            local cnt = 0\n            for i = 1, 3 do\n                print(\"ssl client hello: coro yield: \", cnt)\n                cy()\n                cnt = cnt + 1\n            end\n            return \"coro_done\"\n        end\n\n        local c = cc(coro_func)\n        for i = 1, 4 do\n            print(\"ssl client hello: coro resume, status: \", coroutine.status(c))\n            local ok, res = cr(c)\n            if not ok then\n                print(\"ssl client hello: coro error: \", res)\n                break\n            end\n            if coroutine.status(c) == \"dead\" then\n                print(\"ssl client hello: coro result: \", res)\n                break\n            end\n        end\n\n        print(\"ssl client hello: coroutine test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out\nssl client hello: coroutine test start\nssl client hello: coro resume, status: suspended\nssl client hello: coro yield: 0\nssl client hello: coro resume, status: suspended\nssl client hello: coro yield: 1\nssl client hello: coro resume, status: suspended\nssl client hello: coro yield: 2\nssl client hello: coro resume, status: suspended\nssl client hello: coro result: coro_done\nssl client hello: coroutine test done\ntest completed\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 37: ssl_client_hello_by_lua* without yield API (simple logic)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_client_hello_by_lua_block {\n        print(\"ssl client hello: simple test start\")\n\n        -- Simple calculations without yield\n        local sum = 0\n        for i = 1, 10 do\n            sum = sum + i\n        end\n\n        print(\"ssl client hello: calculated sum: \", sum)\n\n        -- String operations\n        local str = \"hello\"\n        str = str .. \" world\"\n        print(\"ssl client hello: concatenated string: \", str)\n\n        -- Table operations\n        local t = {a = 1, b = 2, c = 3}\n        local count = 0\n        for k, v in pairs(t) do\n            count = count + v\n        end\n        print(\"ssl client hello: table sum: \", count)\n\n        print(\"ssl client hello: simple test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out\nssl client hello: simple test start\nssl client hello: calculated sum: 55\nssl client hello: concatenated string: hello world\nssl client hello: table sum: 6\nssl client hello: simple test done\ntest completed\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 38: ssl_client_hello_by_lua* with multiple network operations (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_client_hello_by_lua_block {\n        print(\"ssl client hello: multiple network test start\")\n\n        -- First TCP operation\n        local sock1 = ngx.socket.tcp()\n        sock1:settimeout(2000)\n        local ok, err = sock1:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if ok then\n            local bytes, err = sock1:send(\"version\\r\\n\")\n            if bytes then\n                local res, err = sock1:receive()\n                if res then\n                    print(\"ssl client hello: TCP1 version: \", res)\n                end\n            end\n            sock1:close()\n        end\n\n        ngx.sleep(0.01)  -- Small delay\n\n        -- Second UDP operation\n        local sock2 = ngx.socket.udp()\n        sock2:settimeout(1000)\n        local ok, err = sock2:setpeername(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if ok then\n            local req = \"\\0\\1\\0\\0\\0\\1\\0\\0version\\r\\n\"\n            local ok, err = sock2:send(req)\n            if ok then\n                local res, err = sock2:receive()\n                if res then\n                    print(\"ssl client hello: UDP version reply length: \", #res)\n                end\n            end\n            sock2:close()\n        end\n\n        print(\"ssl client hello: multiple network test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out eval\n[\nqr/ssl client hello: multiple network test start/,\nqr/ssl client hello: TCP1 version: VERSION/,\nqr/ssl client hello: UDP version reply length: \\d+/,\nqr/ssl client hello: multiple network test done/,\nqr/test completed/,\n]\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 39: ssl_cert_by_lua* and ssl_client_hello_by_lua* with sleep (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}\n--- http_config\n    ssl_certificate_by_lua_block {\n        print(\"cert by: starting with sleep\")\n        local begin = ngx.now()\n        ngx.sleep(0.05)\n        print(\"cert by: slept for \", ngx.now() - begin, \" seconds\")\n    }\n\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_client_hello_by_lua_block {\n        print(\"client hello: starting with sleep\")\n        local begin = ngx.now()\n        ngx.sleep(0.1)\n        print(\"client hello: slept for \", ngx.now() - begin, \" seconds\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out eval\n[\nqr/client hello: starting with sleep/,\nqr/client hello: slept for 0\\.(?:09|1\\d)\\d+/,\nqr/cert by: starting with sleep/,\nqr/cert by: slept for 0\\.0[4-6]\\d+/,\nqr/test completed/,\n]\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 40: ssl_cert_by_lua* and ssl_client_hello_by_lua* with cosocket (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}\n--- http_config\n    ssl_certificate_by_lua_block {\n        print(\"cert by: cosocket test start\")\n\n        local sock = ngx.socket.udp()\n        sock:settimeout(1000)\n\n        local ok, err = sock:setpeername(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if not ok then\n            ngx.log(ngx.ERR, \"cert by: failed to connect to memc: \", err)\n            return\n        end\n\n        local req = \"\\0\\1\\0\\0\\0\\1\\0\\0flush_all\\r\\n\"\n        local ok, err = sock:send(req)\n        if not ok then\n            ngx.log(ngx.ERR, \"cert by: failed to send flush_all to memc: \", err)\n            return\n        end\n\n        local res, err = sock:receive()\n        if not res then\n            ngx.log(ngx.ERR, \"cert by: failed to receive memc reply: \", err)\n            return\n        end\n\n        print(\"cert by: received UDP memc reply of \", #res, \" bytes\")\n        sock:close()\n        print(\"cert by: cosocket test done\")\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_client_hello_by_lua_block {\n        print(\"client hello: cosocket test start\")\n\n        local sock = ngx.socket.tcp()\n        sock:settimeout(2000)\n\n        local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if not ok then\n            ngx.log(ngx.ERR, \"client hello: failed to connect to memc: \", err)\n            return\n        end\n\n        local bytes, err = sock:send(\"version\\r\\n\")\n        if not bytes then\n            ngx.log(ngx.ERR, \"client hello: failed to send version command: \", err)\n            return\n        end\n\n        local res, err = sock:receive()\n        if not res then\n            ngx.log(ngx.ERR, \"client hello: failed to receive memc reply: \", err)\n            return\n        end\n\n        print(\"client hello: received memc reply: \", res)\n        sock:close()\n        print(\"client hello: cosocket test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out eval\n[\nqr/client hello: cosocket test start/,\nqr/client hello: received memc reply: VERSION/,\nqr/client hello: cosocket test done/,\nqr/cert by: cosocket test start/,\nqr/cert by: received UDP memc reply of \\d+ bytes/,\nqr/cert by: cosocket test done/,\nqr/test completed/,\n]\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 41: ssl_cert_by_lua* and ssl_client_hello_by_lua* with timer and uthread (yield API)\n--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}\n--- http_config\n    ssl_certificate_by_lua_block {\n        print(\"cert by: coroutine test start\")\n\n        local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n\n        local function coro_func()\n            local cnt = 0\n            for i = 1, 2 do\n                print(\"cert by: coro yield: \", cnt)\n                cy()\n                cnt = cnt + 1\n            end\n            return \"cert_by_coro_done\"\n        end\n\n        local c = cc(coro_func)\n        for i = 1, 3 do\n            print(\"cert by: coro resume, status: \", coroutine.status(c))\n            local ok, res = cr(c)\n            if not ok then\n                print(\"cert by: coro error: \", res)\n                break\n            end\n            if coroutine.status(c) == \"dead\" then\n                print(\"cert by: coro result: \", res)\n                break\n            end\n        end\n\n        -- Small sleep to allow timer to execute\n        ngx.sleep(0.01)\n        print(\"cert by: coroutine test done\")\n    }\n\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_client_hello_by_lua_block {\n        print(\"client hello: timer and uthread test start\")\n\n        -- Timer test\n        local function timer_handler()\n            print(\"client hello: timer executed\")\n        end\n\n        local ok, err = ngx.timer.at(0, timer_handler)\n        if not ok then\n            ngx.log(ngx.ERR, \"client hello: failed to create timer: \", err)\n            return\n        end\n\n        print(\"client hello: timer created\")\n\n        -- User thread test\n        local function worker()\n            ngx.sleep(0.01)\n            print(\"client hello: uthread worker executed\")\n            return \"client_hello_result\"\n        end\n\n        local t, err = ngx.thread.spawn(worker)\n        if not t then\n            ngx.log(ngx.ERR, \"client hello: failed to spawn thread: \", err)\n            return\n        end\n\n        print(\"client hello: uthread spawned\")\n\n        local ok, res = ngx.thread.wait(t)\n        if not ok then\n            ngx.log(ngx.ERR, \"client hello: failed to wait thread: \", res)\n            return\n        end\n\n        print(\"client hello: uthread result: \", res)\n        print(\"client hello: timer and uthread test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- response_body\ntest completed\n\n--- 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)/\n--- grep_error_log_out eval\n[\nqr/client hello: timer and uthread test start/,\nqr/client hello: timer created/,\nqr/client hello: uthread spawned/,\nqr/client hello: uthread worker executed/,\nqr/client hello: uthread result: client_hello_result/,\nqr/client hello: timer and uthread test done/,\nqr/cert by: coroutine test start/,\nqr/cert by: coro resume, status: suspended/,\nqr/cert by: coro yield: 0/,\nqr/cert by: coro resume, status: suspended/,\nqr/cert by: coro yield: 1/,\nqr/cert by: coro resume, status: suspended/,\nqr/cert by: coro result: cert_by_coro_done/,\nqr/cert by: coroutine test done/,\nqr/client hello: timer executed/,\nqr/test completed/,\n]\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 42: ssl_cert_by_lua* with cosocket (yield API) and ssl_client_hello_by_lua* failure\n--- skip_eval: 8:!$ENV{TEST_NGINX_USE_HTTP3}\n--- http_config\n    ssl_certificate_by_lua_block {\n        print(\"cert by: test start\")\n        ngx.exit(500)\n    }\n--- config\n    server_tokens off;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n    lua_ssl_verify_depth 3;\n\n    ssl_client_hello_by_lua_block {\n        print(\"client hello: cosocket test start\")\n\n        local sock = ngx.socket.tcp()\n        sock:settimeout(2000)\n\n        local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_MEMCACHED_PORT)\n        if not ok then\n            ngx.log(ngx.ERR, \"client hello: failed to connect to memc: \", err)\n            return\n        end\n\n        local bytes, err = sock:send(\"version\\r\\n\")\n        if not bytes then\n            ngx.log(ngx.ERR, \"client hello: failed to send version command: \", err)\n            return\n        end\n\n        local res, err = sock:receive()\n        if not res then\n            ngx.log(ngx.ERR, \"client hello: failed to receive memc reply: \", err)\n            return\n        end\n\n        print(\"client hello: received memc reply: \", res)\n        sock:close()\n        print(\"client hello: cosocket test done\")\n    }\n\n    location /t {\n        content_by_lua_block {\n            print(\"test completed\")\n            ngx.say(\"test completed\")\n        }\n    }\n--- request\nGET /t\n--- ignore_response\n--- curl_error eval\nqr/Connection time/\n--- 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)/\n--- grep_error_log_out eval\n[\nqr/client hello: cosocket test start/,\nqr/client hello: received memc reply: VERSION/,\nqr/client hello: cosocket test done/,\nqr/cert by: test start/,\n]\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n"
  },
  {
    "path": "t/166-worker-thread.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nour $SkipReason;\n\nBEGIN {\n    if ($ENV{TEST_NGINX_EVENT_TYPE}\n        && $ENV{TEST_NGINX_EVENT_TYPE} !~ /^kqueue|epoll|eventport$/)\n    {\n        $SkipReason = \"unavailable for the event type '$ENV{TEST_NGINX_EVENT_TYPE}'\";\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 2);\n\nour $HtmlDir = html_dir;\n\nour $HttpConfig = qq{\n    lua_package_path \"$::HtmlDir/?.lua;./?.lua;;\";\n    lua_worker_thread_vm_pool_size 1;\n};\n\n#no_diff();\n#no_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: hello from worker thread\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return \"hello\"\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : hello\n\n\n\n=== TEST 2: thread_pool not found\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return \"hello\"\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\nfalse : thread pool testpool not found\n\n\n\n=== TEST 3: pass table\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, ok_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\", {[\"hello\"]=\"world\", [1]={[\"embed\"]=1}})\n        ngx.say(ok, \" , \", ok_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello(arg1)\n    if arg1.hello == \"world\" and arg1[1].embed == 1 then\n        return true\n    end\n    return false\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue , true\n\n\n\n=== TEST 4: expecting at least 3 arguments\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, err = ngx.run_worker_thread(\"testpool\")\n        ngx.say(ok, \" : \", err)\n    }\n}\n--- request\nGET /hello\n--- response_body\nfalse : expecting at least 3 arguments\n\n\n\n=== TEST 5: base64\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, base64 = ngx.run_worker_thread(\"testpool\", \"hello\", \"enc\", \"hello\")\n        ngx.say(ok, \" , \", base64 == \"aGVsbG8=\")\n    }\n}\n--- user_files\n>>> hello.lua\nlocal b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\n\nlocal function enc(data)\n    return ((data:gsub('.', function(x)\n        local r,b='',x:byte()\n        for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end\n        return r;\n    end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)\n        if (#x < 6) then return '' end\n        local c=0\n        for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end\n        return b:sub(c+1,c+1)\n    end)..({ '', '==', '=' })[#data%3+1])\nend\n\nreturn {enc=enc}\n--- request\nGET /hello\n--- response_body\ntrue , true\n\n\n\n=== TEST 6: return table\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, ret = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        if ret.hello == \"world\" and ret[1].embed == 1 then\n            ngx.say(ok, \" , \", true)\n        end\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return {[\"hello\"]=\"world\", [1]={[\"embed\"]=1}}\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue , true\n\n\n\n=== TEST 7: unsupported argument type\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local function dummy() end\n        local ok, err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\", dummy)\n        ngx.say(ok, \" : \", err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return \"hello\"\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\nfalse : unsupported Lua type: LUA_TFUNCTION in the argument\n\n\n\n=== TEST 8: multiple return values\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, res1, res2 = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", res1, \" , \", res2)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return \"hello\", 200\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : hello , 200\n\n\n\n=== TEST 9: module not found\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", err)\n    }\n}\n--- request\nGET /hello\n--- response_body_like\nfalse : module 'hello' not found.*\n\n\n\n=== TEST 10: the number of Lua VM exceeds the pool size\n--- no_http2\n--- quic_max_idle_timeout: 5\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval: $::HttpConfig\n--- config\nlocation /foo {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n\nlocation /bar {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, foobar_or_err = ngx.run_worker_thread(\"testpool\", \"foobar\", \"foobar\")\n        ngx.say(ok, \" : \", foobar_or_err)\n    }\n}\n\nlocation /t {\n    set $port $TEST_NGINX_SERVER_PORT;\n\n    content_by_lua_block {\n        local function t(path)\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local req = \"GET \" .. path .. \" HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            local ret, err, part = sock:receive(\"*a\")\n            local _, idx = string.find(ret, \"\\r\\n\\r\\n\");\n            idx = idx + 1\n            ngx.print(string.sub(ret, idx))\n            ok, err = sock:close()\n        end\n\n        local t1 = ngx.thread.spawn(t, \"/foo\")\n        local t2 = ngx.thread.spawn(t, \"/bar\")\n        ngx.thread.wait(t1)\n        ngx.thread.wait(t2)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    os.execute(\"sleep 3\")\n    return \"hello\"\nend\nreturn {hello=hello}\n>>> foobar.lua\nlocal function foobar()\n    return \"foobar\"\nend\nreturn {foobar=foobar}\n--- request\nGET /t\n--- response_body eval\n\"false : no available Lua vm\\ntrue : hello\\n\"\n--- timeout: 10\n\n\n\n=== TEST 11: kill uthread before worker thread callback\n--- no_http2\n--- quic_max_idle_timeout: 10\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval: $::HttpConfig\n--- config\nlocation /foo {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local function t()\n            local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n            ngx.say(ok, \" : \", hello_or_err)\n        end\n        local t1 = ngx.thread.spawn(t)\n        if ngx.var.arg_kill == \"kill\" then\n            ngx.thread.kill(t1)\n            ngx.say(\"killed\")\n        end\n    }\n}\n\nlocation /t {\n    set $port $TEST_NGINX_SERVER_PORT;\n\n    content_by_lua_block {\n        local function t(path)\n            local sock = ngx.socket.tcp()\n            local port = ngx.var.port\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local req = \"GET \" .. path .. \" HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            local ret, err, part = sock:receive(\"*a\")\n            local _, idx = string.find(ret, \"\\r\\n\\r\\n\");\n            idx = idx + 1\n            ngx.print(string.sub(ret, idx))\n            ok, err = sock:close()\n        end\n\n        local t1 = ngx.thread.spawn(t, \"/foo?kill=kill\")\n        ngx.thread.wait(t1)\n        ngx.sleep(4)\n        local t2 = ngx.thread.spawn(t, \"/foo\")\n        ngx.thread.wait(t2)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    os.execute(\"sleep 1\")\n    return \"hello\"\nend\nreturn {hello=hello}\n>>> foobar.lua\nlocal function foobar()\n    return \"foobar\"\nend\nreturn {foobar=foobar}\n--- request\nGET /t\n--- response_body eval\n\"killed\\ntrue : hello\\n\"\n--- timeout: 10\n\n\n\n=== TEST 12: exit before worker thread callback\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local function t()\n            local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n            ngx.say(ok, \" : \", hello_or_err)\n        end\n        ngx.thread.spawn(t)\n        ngx.exit(200)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    os.execute(\"sleep 3\")\n    return \"hello\"\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\n--- timeout: 10\n\n\n\n=== TEST 13: unsupported argument type in nested table\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local function dummy() end\n        local ok, err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\",\n                    {[\"hello\"]=\"world\", [1]={[\"embed\"]=1, [\"dummy\"]=dummy}})\n        ngx.say(ok, \" : \", err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return \"hello\"\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\nfalse : unsupported Lua type: LUA_TFUNCTION in the argument\n\n\n\n=== TEST 14: return table with unsupported type\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, ret = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        if ok == false then\n            ngx.say(\"false\", \" , \", ret)\n        end\n        if ret.hello == \"world\" and ret[1].embed == 1 then\n            ngx.say(ok, \" , \", true)\n        end\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    local function dummy() end\n    return {[\"hello\"]=\"world\", [1]={[\"embed\"]=1, [\"dummy\"]=dummy}}\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\nfalse , unsupported Lua type: LUA_TFUNCTION in the return value\n\n\n\n=== TEST 15: the type of module name is not string\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local function dummy() end\n        local ok, err = ngx.run_worker_thread(\"testpool\", dummy, \"hello\")\n        ngx.say(ok, \" : \", err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return \"hello\"\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\nfalse : module name should be a string\n\n\n\n=== TEST 16: the type of function name is not string\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local function dummy() end\n        local ok, err = ngx.run_worker_thread(\"testpool\", \"hello\", dummy)\n        ngx.say(ok, \" : \", err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return \"hello\"\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\nfalse : function name should be a string\n\n\n\n=== TEST 17: the type of thread pool name is not string\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local function dummy() end\n        local ok, err = ngx.run_worker_thread(dummy, \"hello\", \"hello\")\n        ngx.say(ok, \" : \", err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return \"hello\"\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\nfalse : threadpool should be a string\n\n\n\n=== TEST 18: ngx.encode_base64\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return ngx.encode_base64(\"hello\")\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : aGVsbG8=\n\n\n\n=== TEST 19: ngx.config.subsystem\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return ngx.config.subsystem\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : http\n\n\n\n=== TEST 20: ngx.hmac_sha1\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n  local key = \"thisisverysecretstuff\"\n  local src = \"some string we want to sign\"\n  local digest = ngx.hmac_sha1(key, src)\n  return ngx.encode_base64(digest)\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : R/pvxzHC4NLtj7S+kXFg/NePTmk=\n\n\n\n=== TEST 21: ngx.encode_args\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n  return ngx.encode_args({foo = 3, [\"b r\"] = \"hello world\"})\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body eval\nqr/foo=3&b%20r=hello%20world|b%20r=hello%20world&foo=3/\n\n\n\n=== TEST 22: ngx.decode_args\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, ret = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", ret.a, \" : \", ret.b)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n  local args = \"a=bar&b=foo\"\n  args = ngx.decode_args(args)\n  return args\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : bar : foo\n\n\n\n=== TEST 23: ngx.quote_sql_str\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n    location /hello {\n        content_by_lua '\n          local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\", \"a\\\\026b\\\\026\")\n          ngx.say(ok, \" : \", hello_or_err)\n        ';\n    }\n--- user_files\n>>> hello.lua\nlocal function hello(str)\n  return ngx.quote_sql_str(str)\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : 'a\\Zb\\Z'\n\n\n\n=== TEST 24: ngx.decode_base64\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return ngx.decode_base64(\"aGVsbG8=\")\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : hello\n\n\n\n=== TEST 25: ngx.crc32_short\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return ngx.crc32_short(\"hello, world\")\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : 4289425978\n\n\n\n=== TEST 26: ngx.crc32_long\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return ngx.crc32_long(\"hello, world\")\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : 4289425978\n\n\n\n=== TEST 27: ngx.md5_bin\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    local s = ngx.md5_bin(45)\n    s = string.gsub(s, \".\", function (c)\n            return string.format(\"%02x\", string.byte(c))\n        end)\n    return s\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : 6c8349cc7260ae62e3b1396831a8398f\n\n\n\n=== TEST 28: ngx.md5\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return ngx.md5(\"hello\")\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : 5d41402abc4b2a76b9719d911017c592\n\n\n\n=== TEST 29: ngx.config.debug\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return ngx.config.debug\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body_like chop\n^true : (?:true|false)$\n\n\n\n=== TEST 30: ngx.config.prefix\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return ngx.config.prefix()\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body_like chop\n^true : \\/\\S+$\n\n\n\n=== TEST 31: ngx.config.nginx_version\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return ngx.config.nginx_version\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body_like chop\n^true : \\d+$\n\n\n\n=== TEST 32: ngx.config.nginx_configure\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return ngx.config.nginx_configure()\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body_like chop\n^\\s*\\-\\-[^-]+\n\n\n\n=== TEST 33: ngx.config.ngx_lua_version\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return ngx.config.ngx_lua_version\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body_like chop\n^true : \\d+$\n\n\n\n=== TEST 34: write_log_file\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /write_log_file {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local ok, err = ngx.run_worker_thread(\"testpool\", \"write_log_file\", \"log\", ngx.var.arg_str)\n        if not ok then\n            ngx.say(ok, \" : \", err)\n            return\n        end\n        ngx.say(ok)\n    }\n}\n--- user_files\n>>> write_log_file.lua\nlocal function log(str)\n    local file, err = io.open(\"/tmp/tmp.log\", \"w\")\n    if not file then\n        return false, err\n    end\n    file:write(str)\n    file:flush()\n    file:close()\n    return true\nend\nreturn {log=log}\n--- request\nGET /write_log_file?str=hello\n--- response_body\ntrue\n\n\n\n=== TEST 35: shdict get, int value\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n\"\n    lua_shared_dict dogs 10m;\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n\"\n--- config\nlocation /dictget {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local dogs = ngx.shared.dogs\n        dogs:set(\"Jim\", 8)\n        local ok, err = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictget\")\n        ngx.say(ok, \",\", err)\n    }\n}\n--- user_files\n>>> test_shdict.lua\nlocal function dictget(str)\n    local dogs = ngx.shared.dogs\n    return dogs:get(\"Jim\")\nend\nreturn {dictget=dictget}\n--- request\nGET /dictget\n--- response_body\ntrue,8\n\n\n\n=== TEST 36: shdict set nil in main thread\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n\"\n    lua_shared_dict dogs 10m;\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n\"\n--- config\nlocation /dictget {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local dogs = ngx.shared.dogs\n        dogs:set(\"Jim\", 8)\n        local ok, err = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictget\")\n        ngx.say(ok, \",\", err)\n        dogs:set(\"Jim\", nil)\n        local ok, err = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictget\")\n        ngx.say(ok, \",\", err)\n    }\n}\n--- user_files\n>>> test_shdict.lua\nlocal function dictget(str)\n    local dogs = ngx.shared.dogs\n    return dogs:get(\"Jim\")\nend\nreturn {dictget=dictget}\n--- request\nGET /dictget\n--- response_body\ntrue,8\ntrue,nil\n\n\n\n=== TEST 37: shdict set nil in worker thread\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n\"\n    lua_shared_dict dogs 10m;\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n\"\n--- config\nlocation /dictsetnil {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local dogs = ngx.shared.dogs\n        dogs:set(\"Jim\", 8)\n        local ok, err = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictsetnil\")\n        ngx.say(ok, \",\", err)\n        ngx.say(ok, \",\", dogs:get(\"Jim\"))\n    }\n}\n--- user_files\n>>> test_shdict.lua\nlocal function dictsetnil(str)\n    local dogs = ngx.shared.dogs\n    return dogs:set(\"Jim\", nil)\nend\nreturn {dictsetnil=dictsetnil}\n--- request\nGET /dictsetnil\n--- response_body\ntrue,true\ntrue,nil\n\n\n\n=== TEST 38: shdict get_stale\nFor http3: curl: (55) ngtcp2_conn_handle_expiry returned error: ERR_IDLE_CLOSE\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3}\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n\"\n    lua_shared_dict dogs 10m;\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n\"\n--- config\nlocation /dictget {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local dogs = ngx.shared.dogs\n        dogs:set(\"Jim\", 8, 1)\n        ngx.sleep(2)\n        local ok, err = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictget\")\n        ngx.say(ok, \",\", err)\n    }\n}\n--- user_files\n>>> test_shdict.lua\nlocal function dictget(str)\n    local dogs = ngx.shared.dogs\n    return dogs:get_stale(\"Jim\")\nend\nreturn {dictget=dictget}\n--- request\nGET /dictget\n--- response_body\ntrue,8\n\n\n\n=== TEST 39: shdict add failed\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n\"\n    lua_shared_dict dogs 10m;\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n\"\n--- config\nlocation /dictadd {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local dogs = ngx.shared.dogs\n        dogs:set(\"Jim\", 8)\n        local ok, err, err2 = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictadd\")\n        ngx.say(ok, \",\", err, \",\", err2)\n    }\n}\n--- user_files\n>>> test_shdict.lua\nlocal function dictadd(str)\n    local dogs = ngx.shared.dogs\n    local success, err = dogs:add(\"Jim\", \"hello\")\n    return success, err\nend\nreturn {dictadd=dictadd}\n--- request\nGET /dictadd\n--- response_body\ntrue,false,exists\n\n\n\n=== TEST 40: shdict force add\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n\"\n    lua_shared_dict dogs 6m;\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n\"\n--- config\nlocation /dictadd {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local dogs = ngx.shared.dogs\n        local bigstr = string.rep(\"A\", 1024*1024*3)\n        dogs:set(\"Jim\", bigstr)\n        local ok, ret, err, forcible = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictadd\")\n        ngx.say(ok, \",\", ret, \",\", forcible, \",\", dogs:get(\"Jim\"))\n    }\n}\n--- user_files\n>>> test_shdict.lua\nlocal function dictadd(str)\n    local dogs = ngx.shared.dogs\n    local bigstr = string.rep(\"A\", 1024*1024*5)\n    local success, err, forcible = dogs:add(\"King\", bigstr)\n    return success, err, forcible\nend\nreturn {dictadd=dictadd}\n--- request\nGET /dictadd\n--- response_body\ntrue,true,true,nil\n\n\n\n=== TEST 41: shdict replace\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n\"\n    lua_shared_dict dogs 6m;\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n\"\n--- config\nlocation /dictreplace {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local dogs = ngx.shared.dogs\n        local bigstr = string.rep(\"A\", 1024*1024*3)\n        dogs:set(\"Jim\", bigstr)\n        local ok, ret, err = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictreplace\")\n        ngx.say(ok, \",\", ret, \",\", err, \",\", dogs:get(\"Jim\"))\n    }\n}\n--- user_files\n>>> test_shdict.lua\nlocal function dictreplace(str)\n    local dogs = ngx.shared.dogs\n    local success, err = dogs:replace(\"Jim\", 8)\n    return success, err\nend\nreturn {dictreplace=dictreplace}\n--- request\nGET /dictreplace\n--- response_body\ntrue,true,nil,8\n\n\n\n=== TEST 42: shdict replace not found\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n\"\n    lua_shared_dict dogs 6m;\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n\"\n--- config\nlocation /dictreplace {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local dogs = ngx.shared.dogs\n        local ok, ret, err = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictreplace\")\n        ngx.say(ok, \",\", ret, \",\", err)\n    }\n}\n--- user_files\n>>> test_shdict.lua\nlocal function dictreplace(str)\n    local dogs = ngx.shared.dogs\n    local success, err = dogs:replace(\"Jim\", 8)\n    return success, err\nend\nreturn {dictreplace=dictreplace}\n--- request\nGET /dictreplace\n--- response_body\ntrue,false,not found\n\n\n\n=== TEST 43: shdict incr\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n\"\n    lua_shared_dict dogs 6m;\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n\"\n--- config\nlocation /dictincr {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local dogs = ngx.shared.dogs\n        local success, err = dogs:set(\"Jim\", 8)\n        local ok, ret, err = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictincr\")\n        ngx.say(ok, \",\", ret, \",\", err, \",\", dogs:get(\"Jim\"))\n    }\n}\n--- user_files\n>>> test_shdict.lua\nlocal function dictincr(str)\n    local dogs = ngx.shared.dogs\n    local success, err = dogs:incr(\"Jim\", 1)\n    return success, err\nend\nreturn {dictincr=dictincr}\n--- request\nGET /dictincr\n--- response_body\ntrue,9,nil,9\n\n\n\n=== TEST 44: shdict lpush lpop\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n\"\n    lua_shared_dict dogs 6m;\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n\"\n--- config\nlocation /dictlpush {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local dogs = ngx.shared.dogs\n        dogs:lpush(\"Jim\", 8)\n        dogs:lpush(\"Jim\", 9)\n        local ok, val, len, err = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictlpush\")\n        ngx.say(ok, \",\", val, \",\", len, \",\", err, \",\", dogs:lpop(\"Jim\"))\n    }\n}\n--- user_files\n>>> test_shdict.lua\nlocal function dictlpush(str)\n    local dogs = ngx.shared.dogs\n    local val = dogs:lpop(\"Jim\")\n    local len, err = dogs:lpush(\"Jim\", 7)\n    return val, len, err\nend\nreturn {dictlpush=dictlpush}\n--- request\nGET /dictlpush\n--- response_body\ntrue,9,2,nil,7\n\n\n\n=== TEST 45: shdict expire ttl\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n\"\n    lua_shared_dict dogs 6m;\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n\"\n--- config\nlocation /dictexpire {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local dogs = ngx.shared.dogs\n        dogs:set(\"Jim\", 8)\n        local ok, success, err = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictexpire\")\n        ngx.say(ok, \",\", success, \",\", err, \",\", dogs:ttl(\"Jim\") <= 1)\n    }\n}\n--- user_files\n>>> test_shdict.lua\nlocal function dictexpire(str)\n    local dogs = ngx.shared.dogs\n    local success, err = dogs:expire(\"Jim\", 1)\n    return success, err\nend\nreturn {dictexpire=dictexpire}\n--- request\nGET /dictexpire\n--- response_body\ntrue,true,nil,true\n\n\n\n=== TEST 46: shdict flush_all\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n\"\n    lua_shared_dict dogs 6m;\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n\"\n--- config\nlocation /dictexpire {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local dogs = ngx.shared.dogs\n        dogs:set(\"Jim\", 8)\n        dogs:set(\"King\", 9)\n        local ok = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictexpire\")\n        ngx.say(ok, \",\", dogs:get(\"Jim\"), \",\", dogs:get(\"King\"))\n    }\n}\n--- user_files\n>>> test_shdict.lua\nlocal function dictexpire(str)\n    local dogs = ngx.shared.dogs\n    dogs:flush_all()\nend\nreturn {dictexpire=dictexpire}\n--- request\nGET /dictexpire\n--- response_body\ntrue,nil,nil\n\n\n\n=== TEST 47: shdict get_keys\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n\"\n    lua_shared_dict dogs 6m;\n    lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\n\"\n--- config\nlocation /dictgetkeys {\n    default_type 'text/plain';\n\n    access_by_lua_block {\n        local dogs = ngx.shared.dogs\n        dogs:set(\"Jim\", 8)\n        dogs:set(\"King\", 9)\n        local ok, keys = ngx.run_worker_thread(\"testpool\", \"test_shdict\", \"dictgetkeys\")\n        ngx.say(ok, \",\", table.concat(keys, \":\"))\n    }\n}\n--- user_files\n>>> test_shdict.lua\nlocal function dictgetkeys(str)\n    local dogs = ngx.shared.dogs\n    return dogs:get_keys()\nend\nreturn {dictgetkeys=dictgetkeys}\n--- request\nGET /dictgetkeys\n--- response_body\ntrue,Jim:King\n\n\n\n=== TEST 48: unsupported argument type in self-reference table\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local t = {}\n        t.a = t\n        local ok, ok_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\", t)\n        ngx.say(ok, \" , \", ok_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello(arg1)\n    return true\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\nfalse , suspicious circular references, table depth exceed max depth: 100 in the argument\n\n\n\n=== TEST 49: unsupported argument type in circular-reference table\n--- main_config\n    thread_pool testpool threads=100;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local t = {}\n        local s = {}\n        t.a = s\n        s.b = t\n\n        local ok, ok_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\", t)\n        ngx.say(ok, \" , \", ok_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello(arg1)\n    return true\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\nfalse , suspicious circular references, table depth exceed max depth: 100 in the argument\n\n\n\n=== TEST 50: call run_worker_thread twice\n--- main_config\n    thread_pool testpool threads=1;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n\n        ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", hello_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return \"hello\"\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : hello\ntrue : hello\n\n\n\n=== TEST 51: big object\n--- main_config\n    thread_pool testpool threads=1;\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\nlocation /hello {\n    default_type 'text/plain';\n\n    content_by_lua_block {\n        local ok, hello_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", #hello_or_err)\n\n        local ok, gcsize_or_err = ngx.run_worker_thread(\"testpool\", \"hello\", \"gcsize\")\n        ngx.say(ok, \" : \", gcsize_or_err)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    return string.rep(\"helloworld\", 1000000)\nend\n\nlocal function gcsize()\n    return collectgarbage(\"count\")\nend\n\nreturn {\n    hello = hello,\n    gcsize = gcsize\n}\n--- request\nGET /hello\n--- response_body eval\nqr/\\Atrue : 10000000\ntrue : \\d{3,4}\\.\\d+\n\\z/ms\n"
  },
  {
    "path": "t/167-server-rewrite.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = <<_EOC_;\n$t::StapThread::GCScript\n\nF(ngx_http_lua_check_broken_connection) {\n    println(\"lua check broken conn\")\n}\n\nF(ngx_http_lua_request_cleanup) {\n    println(\"lua req cleanup\")\n}\n_EOC_\n\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 10);\n\n#log_level(\"info\");\n#no_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: server_rewrite_by_lua_block in http\n--- http_config\n    server_rewrite_by_lua_block {\n        ngx.ctx.a = \"server_rewrite_by_lua_block in http\"\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(ngx.ctx.a)\n            ngx.log(ngx.INFO, ngx.ctx.a)\n        }\n    }\n--- request\nGET /lua\n--- response_body\nserver_rewrite_by_lua_block in http\n--- error_log\nserver_rewrite_by_lua_block in http\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: server_rewrite_by_lua_block in server\n--- config\n    server_rewrite_by_lua_block {\n        ngx.log(ngx.INFO, \"server_rewrite_by_lua_block in server\")\n    }\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\nOK\n--- error_log\nserver_rewrite_by_lua_block in server\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: redirect\n--- config\n    server_rewrite_by_lua_block {\n        ngx.redirect(\"/foo\")\n    }\n--- request\nGET /lua\n--- raw_response_headers_like eval\nqr{[Ll]ocation: /foo\\r\\n}\n--- response_body_like: 302 Found\n--- error_code: 302\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: flush\n--- config\n    server_rewrite_by_lua_block {\n        ngx.say(\"foo\")\n        ngx.flush(true)\n    }\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\nfoo\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: eof\n--- config\n    server_rewrite_by_lua_block {\n        ngx.say(\"foo\")\n        ngx.eof()\n    }\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\nfoo\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: send_headers\n--- config\n    server_rewrite_by_lua_block {\n        ngx.header[\"Foox\"] = {\"conx1\", \"conx2\" }\n        ngx.header[\"Fooy\"] = {\"cony1\", \"cony2\" }\n        ngx.send_headers()\n    }\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\n--- response_headers\nFoox: conx1, conx2\nFooy: cony1, cony2\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: read_body\n--- config\n    server_rewrite_by_lua_block {\n        ngx.req.read_body()\n        ngx.say(ngx.var.request_body)\n    }\n--- request\nPOST /lua\nhello, world\n--- response_body\nhello, world\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: req_sock\n--- config\n    server_rewrite_by_lua_block {\n        local sock = ngx.req.socket()\n            sock:receive(2)\n            sock:receive(2)\n            sock:receive(1)\n            ngx.sleep(1)\n    }\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n--- request\nPOST /lua\nhello\n\n--- stap2 eval: $::StapScript\n--- stap eval: $::GCScript\n--- stap_out\nlua check broken conn\nlua check broken conn\nlua req cleanup\ndelete thread 1\n\n--- wait: 1\n--- timeout: 0.2\n--- abort\n--- ignore_response\n--- no_error_log\n[error]\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3}\n\n\n\n=== TEST 9: rewrite args (not break cycle by default)\n--- config\n    location /bar {\n        echo \"bar: $uri?$args\";\n    }\n    server_rewrite_by_lua_block {\n        if ngx.var.uri ~= \"/bar\" then\n            ngx.req.set_uri_args(\"hello\")\n            ngx.req.set_uri(\"/bar\", true)\n        end\n    }\n    location /foo {\n\n        echo \"foo: $uri?$args\";\n    }\n--- request\n    GET /foo?world\n--- response_body\nbar: /bar?hello\n\n\n\n=== TEST 10: server_rewrite_by_lua_block overwrite by server\n--- http_config\n    server_rewrite_by_lua_block {\n        ngx.log(ngx.INFO, \"server_rewrite_by_lua_block in http\")\n    }\n--- config\n    server_rewrite_by_lua_block {\n        ngx.log(ngx.INFO, \"server_rewrite_by_lua_block in server\")\n    }\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\nOK\n--- error_log\nserver_rewrite_by_lua_block in server\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: sleep\n--- config\n    server_rewrite_by_lua_block {\n        ngx.sleep(0.001)\n        ngx.log(ngx.INFO, \"server_rewrite_by_lua_block in server\")\n    }\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\nOK\n--- error_log\nserver_rewrite_by_lua_block in server\n--- no_error_log\n[error]\n\n\n\n=== TEST 12: ngx.exit(ngx.OK)\n--- config\n    server_rewrite_by_lua_block {\n        ngx.log(ngx.INFO, \"ngx.exit\")\n        ngx.exit(ngx.OK)\n    }\n    location /lua {\n        content_by_lua_block {\n        ngx.say(\"OK\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\nOK\n--- error_log\nngx.exit\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: ngx.exit(503)\n--- config\n    server_rewrite_by_lua_block {\n        ngx.exit(503)\n    }\n    location /lua {\n        content_by_lua_block {\n         ngx.log(ngx.ERR, \"content_by_lua\")\n         ngx.say(\"OK\")\n        }\n    }\n--- request\nGET /lua\n--- error_code: 503\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: subrequests\n--- config\n    server_rewrite_by_lua_block {\n        ngx.log(ngx.INFO, \"is_subrequest:\", ngx.is_subrequest)\n    }\n\n    location /lua {\n        content_by_lua_block {\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n        }\n    }\n\n    location /sub {\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n\n--- request\nGET /lua\n--- response_body\nOK\n--- error_log\nis_subrequest:false\nis_subrequest:true\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: rewrite by ngx_http_rewrite_module\n--- config\n    server_rewrite_by_lua_block {\n        ngx.log(ngx.INFO, \"uri is \", ngx.var.uri)\n    }\n\n    rewrite ^ /re;\n\n    location /re {\n        content_by_lua_block {\n            ngx.say(\"RE\")\n        }\n    }\n\n    location /ok {\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n\n--- request\nGET /lua\n--- response_body\nRE\n--- error_log\nuri is /lua\n--- no_error_log\n[error]\n\n\n\n=== TEST 16: exec\n--- config\n    server_rewrite_by_lua_block {\n        if ngx.var.uri ~= \"/ok\" then\n            ngx.exec(\"/ok\")\n        end\n        ngx.log(ngx.INFO, \"uri is \", ngx.var.uri)\n    }\n\n    location /ok {\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n\n--- request\nGET /lua\n--- response_body\nOK\n--- error_log\nuri is /ok\n--- no_error_log\n[error]\n\n\n\n=== TEST 17: server_rewrite_by_lua and rewrite_by_lua\n--- http_config\n    server_rewrite_by_lua_block {\n        ngx.log(ngx.INFO, \"server_rewrite_by_lua_block in http\")\n    }\n--- config\n    location /lua {\n        rewrite_by_lua_block {\n            ngx.log(ngx.INFO, \"rewrite_by_lua_block in location\")\n        }\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\nOK\n--- error_log\nserver_rewrite_by_lua_block in http\nrewrite_by_lua_block in location\n--- no_error_log\n[error]\n\n\n\n=== TEST 18: server_rewrite_by_lua_file\n--- http_config\n    server_rewrite_by_lua_file 'html/foo.lua';\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n--- request\nGET /lua\n--- user_files\n>>> foo.lua\nngx.log(ngx.INFO, \"rewrite_by_lua_file in server\")\n--- response_body\nOK\n--- error_log\nrewrite_by_lua_file in server\n--- no_error_log\n[error]\n\n\n\n=== TEST 19: syntax error server_rewrite_by_lua_block in http\n--- http_config\n    server_rewrite_by_lua_block {\n        'for end';\n    }\n--- config\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"OK\")\n        }\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nfailed to load inlined Lua code: server_rewrite_by_lua(nginx.conf:25):2: unexpected symbol near ''for end''\n--- no_error_log\nno_such_error\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HUP}\n\n\n\n=== TEST 20: syntax error server_rewrite_by_lua_block in server\n--- config\n    server_rewrite_by_lua_block {\n        'for end';\n    }\n    location /lua {\n        content_by_lua_block {\n            ngx.say(\"Hello world\")\n        }\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nfailed to load inlined Lua code: server_rewrite_by_lua(nginx.conf:39):2: unexpected symbol near ''for end''\n--- no_error_log\nno_such_error\n"
  },
  {
    "path": "t/168-tcp-socket-bind.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\n# more times than usual(2) for test case 6\nrepeat_each(4);\n\nplan tests => repeat_each() * (blocks() * 3 + 7);\n\nour $HtmlDir = html_dir;\n\n# get ip address in the dev which is default route outgoing dev\nmy $dev = `ip route | awk '/default/ {printf \"%s\", \\$5}'`;\nmy $local_ip = `ip route | grep $dev | grep -o \"src .*\" | head -n 1 | awk '{print \\$2}'`;\nchomp $local_ip;\n\n$ENV{TEST_NGINX_HTML_DIR} = $HtmlDir;\n$ENV{TEST_NGINX_NOT_EXIST_IP} ||= '8.8.8.8';\n$ENV{TEST_NGINX_INVALID_IP} ||= '127.0.0.1:8899';\n$ENV{TEST_NGINX_SERVER_IP} ||= $local_ip;\n\nno_long_string();\n#no_diff();\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_shuffle();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: upstream sockets bind 127.0.0.1\n--- config\n   server_tokens off;\n   location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local ip = \"127.0.0.1\"\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:bind(ip)\n            if not ok then\n                ngx.say(\"failed to bind\", err)\n                return\n            end\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local bytes, err = sock:send(\"GET /foo HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: keepalive\\r\\n\\r\\n\")\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent\")\n\n            local reader = sock:receiveuntil(\"\\r\\n0\\r\\n\\r\\n\")\n            local data, err = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response\")\n            local remote_ip = string.match(data, \"(bind: %d+%.%d+%.%d+%.%d+)\")\n            ngx.say(remote_ip)\n\n            ngx.say(\"done\")\n        }\n    }\n\n    location /foo {\n        echo bind: $remote_addr;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent\nreceived response\nbind: 127.0.0.1\ndone\n--- no_error_log\n[\"[error]\",\n\"bind(127.0.0.1) failed\"]\n--- error_log eval\n\"lua tcp socket bind ip: 127.0.0.1\"\n\n\n\n=== TEST 2: upstream sockets bind server ip, not 127.0.0.1\n--- config\n   server_tokens off;\n   location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local ip = \"$TEST_NGINX_SERVER_IP\"\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:bind(ip)\n            if not ok then\n                ngx.say(\"failed to bind\", err)\n                return\n            end\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local bytes, err = sock:send(\"GET /foo HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: keepalive\\r\\n\\r\\n\")\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            ngx.say(\"request sent\")\n\n            local reader = sock:receiveuntil(\"\\r\\n0\\r\\n\\r\\n\")\n            local data, err = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n\n            ngx.say(\"received response\")\n            local remote_ip = string.match(data, \"(bind: %d+%.%d+%.%d+%.%d+)\")\n            if remote_ip == \"bind: $TEST_NGINX_SERVER_IP\" then\n                ngx.say(\"ip matched\")\n            end\n\n            ngx.say(\"done\")\n        }\n    }\n\n    location /foo {\n        echo bind: $remote_addr;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nrequest sent\nreceived response\nip matched\ndone\n--- no_error_log eval\n[\"[error]\",\n\"bind($ENV{TEST_NGINX_SERVER_IP}) failed\"]\n--- error_log eval\n\"lua tcp socket bind ip: $ENV{TEST_NGINX_SERVER_IP}\"\n\n\n\n=== TEST 3: add setkeepalive\n--- http_config eval\n    \"lua_package_path '$::HtmlDir/?.lua;./?.lua;;';\"\n--- config\n   server_tokens off;\n   location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local test = require \"test\"\n            local t1 = test.go()\n            local t2 = test.go()\n            ngx.say(\"t2 - t1: \", t2 - t1)\n        }\n    }\n--- user_files\n>>> test.lua\nlocal _M = {}\n\nfunction _M.go()\n    local ip = \"127.0.0.1\"\n    local port = ngx.var.port\n\n    local sock = ngx.socket.tcp()\n    local ok, err = sock:bind(ip)\n    if not ok then\n        ngx.say(\"failed to bind\", err)\n        return\n    end\n\n    ngx.say(\"bind: \", ip)\n\n    local ok, err = sock:connect(\"127.0.0.1\", port)\n    if not ok then\n        ngx.say(\"failed to connect: \", err)\n        return\n    end\n\n    ngx.say(\"connected: \", ok)\n\n    local reused = sock:getreusedtimes()\n\n    local ok, err = sock:setkeepalive()\n    if not ok then\n        ngx.say(\"failed to set reusable: \", err)\n    end\n\n    return reused\nend\n\nreturn _M\n--- request\nGET /t\n--- response_body\nbind: 127.0.0.1\nconnected: 1\nbind: 127.0.0.1\nconnected: 1\nt2 - t1: 1\n--- no_error_log\n[\"[error]\",\n\"bind(127.0.0.1) failed\"]\n--- error_log eval\n\"lua tcp socket bind ip: 127.0.0.1\"\n\n\n\n=== TEST 4: upstream sockets bind not exist ip\n--- config\n   server_tokens off;\n   location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local ip = \"$TEST_NGINX_NOT_EXIST_IP\"\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:bind(ip)\n            if not ok then\n                ngx.say(\"failed to bind\", err)\n                return\n            end\n\n            ngx.say(\"bind: \", ip)\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        }\n    }\n--- request\nGET /t\n--- response_body\nbind: 8.8.8.8\nfailed to connect: cannot assign requested address\n--- error_log eval\n[\"bind(8.8.8.8) failed\",\n\"lua tcp socket bind ip: 8.8.8.8\"]\n\n\n\n=== TEST 5: upstream sockets bind invalid ip\n--- config\n   server_tokens off;\n   location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local ip = \"$TEST_NGINX_INVALID_IP\"\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:bind(ip)\n            if not ok then\n                ngx.say(\"failed to bind: \", err)\n                return\n            end\n\n            ngx.say(\"bind: \", ip)\n\n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n        }\n    }\n--- request\nGET /t\n--- response_body\nfailed to bind: bad address\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: tcpsock across request after bind\n--- http_config\n    init_worker_by_lua_block {\n        -- this is not the recommend way, just for test\n        local function tcp()\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:bind(\"127.0.0.1\")\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to bind\")\n            end\n\n            package.loaded.share_sock = sock\n        end\n\n        local ok, err = ngx.timer.at(0, tcp)\n        if not ok then\n            ngx.log(ngx.ERR, \"failed to create timer\")\n        end\n    }\n--- config\n   server_tokens off;\n   location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local port = ngx.var.port\n\n            -- make sure share_sock is created\n            ngx.sleep(0.002)\n\n            local sock = package.loaded.share_sock\n            if sock ~= nil then\n                package.loaded.share_sock = nil\n\n                local ok, err = sock:connect(\"127.0.0.1\", port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                sock:close()\n                collectgarbage(\"collect\")\n            else\n                -- the sock from package.loaded.share_sock is just\n                -- for the first request after worker init\n                -- add following code to keep the same result for other request\n                ngx.say(\"connected: \", 1)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: upstream sockets bind with ip port\n--- config\n   server_tokens off;\n   location /t {\n        set $port $TEST_NGINX_SERVER_PORT;\n        content_by_lua_block {\n            local ip = \"127.0.0.1\"\n            local port = ngx.var.port\n\n            local sock = ngx.socket.tcp()\n\n            local ok, err = sock:bind(ip, 12345)\n            if not ok then\n                ngx.say(\"failed to bind\", err)\n                return\n            end\n  \n            local ok, err = sock:connect(\"127.0.0.1\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local ok, err = sock:setoption(\"reuseaddr\", 1)\n            if not ok then\n                ngx.say(\"setoption reuseaddr failed: \", err)\n            end                  \n\n            ngx.say(\"connected: \", ok)\n\n            local bytes, err = sock:send(\"GET /foo HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: keepalive\\r\\n\\r\\n\")\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            local reader = sock:receiveuntil(\"\\r\\n0\\r\\n\\r\\n\")\n            local data, err = reader()\n\n            if not data then\n                ngx.say(\"failed to receive response body: \", err)\n                return\n            end\n            sock:close()\n            ngx.say(data)\n\n        }\n    }\n\n    location /foo {\n        echo bind: $remote_addr:$remote_port;\n    }\n--- request\nGET /t\n--- response_body eval\nqr/bind:\\s127\\.0\\.0\\.1:12345|failed\\s+to\\s+connect:\\s+address\\s+already\\s+in\\s+use/\n--- error_log eval\n\"lua tcp socket bind ip: 127.0.0.1\"\n"
  },
  {
    "path": "t/169-proxy-ssl-verify.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(3);\n\n# All these tests need to have new openssl\nmy $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';\nmy $openssl_version = eval { `$NginxBinary -V 2>&1` };\n\nif ($openssl_version =~ m/built with OpenSSL (\\d+)\\.(\\d+)\\.(\\d+)/) {\n    my ($major, $minor, $patch) = ($1, $2, $3);\n\n    if ($major < 3 || ($major == 3 && $minor == 0 && $patch < 2)) {\n        plan(skip_all => \"too old OpenSSL, need >= 3.0.2, was \" .\n            \"$major.$minor.$patch\");\n    } else {\n        plan tests => repeat_each() * (blocks() * 5 + 19);\n    }\n} elsif ($openssl_version =~ m/running with BoringSSL/) {\n    plan(skip_all => \"does not support BoringSSL\");\n} else {\n    die \"unknown SSL\";\n}\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_QUIC_IDLE_TIMEOUT} ||= 0.6;\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: invalid proxy_pass url\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"hello world\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                  http://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n\n        proxy_ssl_verify_by_lua_block {\n            ngx.log(ngx.INFO, \"hello world\")\n        }\n    }\n--- request\nGET /t\n--- error_log\nproxy_ssl_verify_by_lua* should be used with proxy_pass https url\n--- must_die\n\n\n\n=== TEST 2: proxy_ssl_verify_by_lua in http {} block\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"hello world\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n\n    proxy_ssl_verify_by_lua_block {\n        ngx.log(ngx.INFO, \"hello world\")\n    }\n--- config\n    location /t {\n        proxy_pass                  http://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n    }\n--- request\nGET /t\n--- error_log\n\"proxy_ssl_verify_by_lua_block\" directive is not allowed here\n--- must_die\n\n\n\n=== TEST 3: proxy_ssl_verify_by_lua in server {} block\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"hello world\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n\n--- config\n    proxy_ssl_verify_by_lua_block {\n        ngx.log(ngx.INFO, \"hello world\")\n    }\n\n    location /t {\n        proxy_pass                  http://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n    }\n--- request\nGET /t\n--- error_log\n\"proxy_ssl_verify_by_lua_block\" directive is not allowed here\n--- must_die\n\n\n\n=== TEST 4: simple logging\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_verify_by_lua_block {\n            ngx.log(ngx.INFO, \"proxy ssl verify by lua is running!\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nsimple logging return\n--- error_log\nproxy ssl verify by lua is running!\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 5: sleep\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"sleep\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_verify_by_lua_block {\n            local begin = ngx.now()\n            ngx.sleep(0.1)\n            print(\"elapsed in proxy ssl verify by lua: \", ngx.now() - begin)\n        }\n    }\n--- request\nGET /t\n--- response_body\nsleep\n--- error_log eval\nqr/elapsed in proxy ssl verify by lua: 0.(?:09|1\\d)\\d+ while loading proxy ssl verify by lua,/,\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 6: timer\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"timer\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_verify_by_lua_block {\n            local function f()\n                print(\"my timer run!\")\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to create timer: \", err)\n                return\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\ntimer\n--- error_log\nmy timer run!\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 7: ngx.exit(0) - no yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"ngx.exit(0) no yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_verify_by_lua_block {\n            ngx.exit(0)\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nngx.exit(0) no yield\n--- error_log\nlua exit with code 0\n--- no_error_log\nshould never reached here\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 8: ngx.exit(ngx.ERROR) - no yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"ngx.exit(ngx.ERROR) no yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_block {\n            ngx.exit(ngx.ERROR)\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 502\n--- error_log eval\n[\n'lua exit with code -1',\n'proxy_ssl_verify_by_lua: handler return value: -1, cert verify callback exit code: 0',\nqr/.*? SSL_do_handshake\\(\\) failed .*?certificate verify failed/,\n]\n--- no_error_log\nshould never reached here\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 9: ngx.exit(0) -  yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"ngx.exit(0) yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_block {\n            ngx.sleep(0.001)\n            ngx.exit(0)\n\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nngx.exit(0) yield\n--- error_log\nlua exit with code 0\n--- no_error_log\nshould never reached here\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 10: ngx.exit(ngx.ERROR) - yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"ngx.exit(ngx.ERROR) yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_block {\n            ngx.sleep(0.001)\n            ngx.exit(ngx.ERROR)\n\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 502\n--- error_log eval\n[\n'lua exit with code -1',\n'proxy_ssl_verify_by_lua: cert verify callback exit code: 0',\nqr/.*? SSL_do_handshake\\(\\) failed .*?certificate verify failed/,\n]\n--- no_error_log\nshould never reached here\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 11: lua exception - no yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"lua exception - no yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_block {\n            error(\"bad bad bad\")\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 502\n--- error_log eval\n[\n'runtime error: proxy_ssl_verify_by_lua(nginx.conf:65):2: bad bad bad',\n'proxy_ssl_verify_by_lua: handler return value: 500, cert verify callback exit code: 0',\nqr/.*? SSL_do_handshake\\(\\) failed .*?certificate verify failed/,\n]\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 12: lua exception - yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"lua exception - yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_block {\n            ngx.sleep(0.001)\n            error(\"bad bad bad\")\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 502\n--- error_log eval\n[\n'runtime error: proxy_ssl_verify_by_lua(nginx.conf:65):3: bad bad bad',\n'proxy_ssl_verify_by_lua: cert verify callback exit code: 0',\nqr/.*? SSL_do_handshake\\(\\) failed .*?certificate verify failed/,\n]\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 13: get phase\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"get phase return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_block {\n            print(\"get_phase: \", ngx.get_phase())\n        }\n    }\n--- request\nGET /t\n--- response_body\nget phase return\n--- error_log\nget_phase: proxy_ssl_verify\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 14: subrequests disabled\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"subrequests disabled\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_block {\n            ngx.location.capture(\"/foo\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 502\n--- error_log eval\n[\n'proxy_ssl_verify_by_lua(nginx.conf:65):2: API disabled in the context of proxy_ssl_verify_by_lua*',\n'proxy_ssl_verify_by_lua: handler return value: 500, cert verify callback exit code: 0',\nqr/.*? SSL_do_handshake\\(\\) failed .*?certificate verify failed/,\n]\n--- no_error_log\n[alert]\n\n\n\n=== TEST 15: simple logging (by_lua_file)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging by lua file\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- user_files\n>>> a.lua\nprint(\"proxy ssl verify by lua is running!\")\n\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_file html/a.lua;\n    }\n--- request\nGET /t\n--- response_body\nsimple logging by lua file\n--- error_log\na.lua:1: proxy ssl verify by lua is running!\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 16: coroutine API\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"coroutine API\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_block {\n            local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n\n            local function f()\n                local cnt = 0\n                for i = 1, 20 do\n                    print(\"co yield: \", cnt)\n                    cy()\n                    cnt = cnt + 1\n                end\n            end\n\n            local c = cc(f)\n            for i = 1, 3 do\n                print(\"co resume, status: \", coroutine.status(c))\n                cr(c)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\ncoroutine API\n--- grep_error_log eval: qr/co (?:yield: \\d+|resume, status: \\w+)/\n--- grep_error_log_out\nco resume, status: suspended\nco yield: 0\nco resume, status: suspended\nco yield: 1\nco resume, status: suspended\nco yield: 2\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 17: simple user thread wait with yielding\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple user thread wait with yielding\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_block {\n            local function f()\n                ngx.sleep(0.01)\n                print(\"uthread: hello in thread\")\n                return \"done\"\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.log(ngx.ERR, \"uthread: failed to spawn thread: \", err)\n                return ngx.exit(ngx.ERROR)\n            end\n\n            print(\"uthread: thread created: \", coroutine.status(t))\n\n            local ok, res = ngx.thread.wait(t)\n            if not ok then\n                print(\"uthread: failed to wait thread: \", res)\n                return\n            end\n\n            print(\"uthread: \", res)\n        }\n    }\n--- request\nGET /t\n--- response_body\nsimple user thread wait with yielding\n--- no_error_log\n[error]\n[alert]\n--- grep_error_log eval: qr/uthread: [^.,]+/\n--- grep_error_log_out\nuthread: thread created: running while loading proxy ssl verify by lua\nuthread: hello in thread while loading proxy ssl verify by lua\nuthread: done while loading proxy ssl verify by lua\n\n\n\n=== TEST 18: uthread (kill)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"uthread (kill)\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_block {\n            local function f()\n                ngx.log(ngx.INFO, \"uthread: hello from f()\")\n                ngx.sleep(1)\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.log(ngx.ERR, \"failed to spawn thread: \", err)\n                return ngx.exit(ngx.ERROR)\n            end\n\n            local ok, res = ngx.thread.kill(t)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to kill thread: \", res)\n                return\n            end\n\n            ngx.log(ngx.INFO, \"uthread: killed\")\n\n            local ok, err = ngx.thread.kill(t)\n            if not ok then\n                ngx.log(ngx.INFO, \"uthread: failed to kill: \", err)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\nuthread (kill)\n--- no_error_log\n[error]\n[alert]\n[emerg]\n--- grep_error_log eval: qr/uthread: [^.,]+/\n--- grep_error_log_out\nuthread: hello from f() while loading proxy ssl verify by lua\nuthread: killed while loading proxy ssl verify by lua\nuthread: failed to kill: already waited or killed while loading proxy ssl verify by lua\n\n\n\n=== TEST 19: ngx.exit(ngx.OK) - no yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"ngx.exit(ngx.OK) - no yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_block {\n            ngx.exit(ngx.OK)\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nngx.exit(ngx.OK) - no yield\n--- error_log eval\n[\n'proxy_ssl_verify_by_lua: handler return value: 0, cert verify callback exit code: 1',\nqr/\\[debug\\] .*? SSL_do_handshake: 1/,\n'lua exit with code 0',\n]\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 20: proxy_ssl_verify_by_lua* without yield API (simple logic)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"without yield API, simple logic\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_block {\n            print(\"proxy ssl verify: simple test start\")\n\n            -- Simple calculations without yield\n            local sum = 0\n            for i = 1, 10 do\n                sum = sum + i\n            end\n\n            print(\"proxy ssl verify: calculated sum: \", sum)\n\n            -- String operations\n            local str = \"hello\"\n            str = str .. \" world\"\n            print(\"proxy ssl verify: concatenated string: \", str)\n\n            -- Table operations\n            local t = {a = 1, b = 2, c = 3}\n            local count = 0\n            for k, v in pairs(t) do\n                count = count + v\n            end\n            print(\"proxy ssl verify: table sum: \", count)\n\n            print(\"proxy ssl verify: simple test done\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nwithout yield API, simple logic\n--- 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)/\n--- grep_error_log_out\nproxy ssl verify: simple test start\nproxy ssl verify: calculated sum: 55\nproxy ssl verify: concatenated string: hello world\nproxy ssl verify: table sum: 6\nproxy ssl verify: simple test done\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 21: lua_upstream_skip_openssl_default_verify default off\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"lua_upstream_skip_openssl_default_verify default off\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_verify_by_lua_block {\n            ngx.log(ngx.INFO, \"proxy ssl verify by lua is running!\")\n        }\n    }\n--- request\nGET /t\n--- error_log\nproxy_ssl_verify_by_lua: openssl default verify\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 22: lua_upstream_skip_openssl_default_verify on\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"lua_upstream_skip_openssl_default_verify default off\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        lua_upstream_skip_openssl_default_verify on;\n\n        proxy_ssl_verify_by_lua_block {\n            ngx.log(ngx.INFO, \"proxy ssl verify by lua is running!\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nlua_upstream_skip_openssl_default_verify default off\n--- error_log\nproxy ssl verify by lua is running!\n--- no_error_log\nproxy_ssl_verify_by_lua: openssl default verify\n[error]\n[alert]\n\n\n\n=== TEST 23: ngx.ctx to pass data from downstream phase to upstream phase\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        rewrite_by_lua_block {\n            ngx.ctx.greeting = \"I am from rewrite phase\"\n        }\n\n        proxy_ssl_verify_by_lua_block {\n            ngx.log(ngx.INFO, \"greeting: \", ngx.ctx.greeting)\n        }\n    }\n--- request\nGET /t\n--- response_body\nsimple logging return\n--- error_log\ngreeting: I am from rewrite phase\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 24: upstream connection aborted\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"hello world\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_connect_timeout         100ms;\n\n        proxy_ssl_verify_by_lua_block {\n            ngx.sleep(0.2)\n        }\n    }\n--- request\nGET /t\n--- error_code: 504\n--- response_body_like: 504 Gateway Time-out\n--- error_log\nupstream timed out (110: Connection timed out) while loading proxy ssl verify by lua\nproxy_ssl_verify_by_lua: cert verify callback aborted\n--- no_error_log\n[alert]\n--- wait: 0.5\n\n\n\n=== TEST 25: cosocket\n--- http_config\n    server {\n        listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1;\n        server_name test.com;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {\n                ngx.sleep(0.1)\n\n                ngx.status = 201\n                ngx.say(\"foo\")\n                ngx.exit(201)\n            }\n            more_clear_headers Date;\n        }\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_verify_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_RAND_PORT_1)\n                if not ok then\n                    ngx.log(ngx.ERR, \"failed to connect: \", err)\n                    return\n                end\n\n                ngx.log(ngx.INFO, \"connected: \", ok)\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.log(ngx.ERR, \"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.log(ngx.INFO, \"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.log(ngx.ERR, \"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.log(ngx.INFO, \"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.log(ngx.INFO, \"close: \", ok, \" \", err)\n            end -- do\n            -- collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nsimple logging return\n--- error_log\nconnected: 1\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived:\nreceived: foo\nclose: 1 nil\n--- no_error_log\n[error]\n[alert]\n"
  },
  {
    "path": "t/170-proxy-ssl-cert.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(3);\n\n# All these tests need to have new openssl\nmy $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';\nmy $openssl_version = eval { `$NginxBinary -V 2>&1` };\n\nif ($openssl_version =~ m/BoringSSL/) {\n    plan(skip_all => \"does not support BoringSSL\");\n} elsif ($openssl_version =~ m/built with OpenSSL (0|1\\.0\\.(?:0|1[^\\d]|2[a-d]).*)/) {\n    plan(skip_all => \"too old OpenSSL, need >= 1.0.2e, was $1\");\n} else {\n    plan tests => repeat_each() * (blocks() * 5 + 17);\n}\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_QUIC_IDLE_TIMEOUT} ||= 0.6;\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: invalid proxy_pass url\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"hello world\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                  http://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n\n        proxy_ssl_certificate_by_lua_block {\n            ngx.log(ngx.INFO, \"hello world\")\n        }\n    }\n--- request\nGET /t\n--- error_log\nproxy_ssl_certificate_by_lua* should be used with proxy_pass https url\n--- must_die\n\n\n\n=== TEST 2: proxy_ssl_certificate_by_lua in http {} block\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"hello world\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n\n    proxy_ssl_certificate_by_lua_block {\n        ngx.log(ngx.INFO, \"hello world\")\n    }\n--- config\n    location /t {\n        proxy_pass                  http://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n    }\n--- request\nGET /t\n--- error_log\n\"proxy_ssl_certificate_by_lua_block\" directive is not allowed here\n--- must_die\n\n\n\n=== TEST 3: proxy_ssl_certificate_by_lua in server {} block\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"hello world\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n\n--- config\n    proxy_ssl_certificate_by_lua_block {\n        ngx.log(ngx.INFO, \"hello world\")\n    }\n\n    location /t {\n        proxy_pass                  http://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n    }\n--- request\nGET /t\n--- error_log\n\"proxy_ssl_certificate_by_lua_block\" directive is not allowed here\n--- must_die\n\n\n\n=== TEST 4: simple logging\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_certificate_by_lua_block {\n            ngx.log(ngx.INFO, \"proxy ssl certificate by lua is running!\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nsimple logging return\n--- error_log\nproxy ssl certificate by lua is running!\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 5: sleep\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"sleep\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_certificate_by_lua_block {\n            local begin = ngx.now()\n            ngx.sleep(0.1)\n            print(\"elapsed in proxy ssl certificate by lua: \", ngx.now() - begin)\n        }\n    }\n--- request\nGET /t\n--- response_body\nsleep\n--- error_log eval\nqr/elapsed in proxy ssl certificate by lua: 0.(?:09|1\\d)\\d+ while loading proxy ssl certificate by lua,/,\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 6: timer\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"timer\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_certificate_by_lua_block {\n            local function f()\n                print(\"my timer run!\")\n            end\n            local ok, err = ngx.timer.at(0, f)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to create timer: \", err)\n                return\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\ntimer\n--- error_log\nmy timer run!\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 7: ngx.exit(0) - no yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"ngx.exit(0) no yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_certificate_by_lua_block {\n            ngx.exit(0)\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nngx.exit(0) no yield\n--- error_log\nlua exit with code 0\n--- no_error_log\nshould never reached here\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 8: ngx.exit(ngx.ERROR) - no yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"ngx.exit(ngx.ERROR) no yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_certificate_by_lua_block {\n            ngx.exit(ngx.ERROR)\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 502\n--- error_log eval\n[\n'lua exit with code -1',\n'proxy_ssl_certificate_by_lua: handler return value: -1, cert cb exit code: 0',\nqr/.*? SSL_do_handshake\\(\\) failed .*?callback failed/,\n]\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 9: ngx.exit(0) -  yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"ngx.exit(0) yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_certificate_by_lua_block {\n            ngx.sleep(0.001)\n            ngx.exit(0)\n\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nngx.exit(0) yield\n--- error_log\nlua exit with code 0\n--- no_error_log\nshould never reached here\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 10: ngx.exit(ngx.ERROR) - yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"ngx.exit(ngx.ERROR) yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_certificate_by_lua_block {\n            ngx.sleep(0.001)\n            ngx.exit(ngx.ERROR)\n\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 502\n--- error_log eval\n[\n'lua exit with code -1',\n'proxy_ssl_certificate_by_lua: cert cb exit code: 0',\nqr/.*? SSL_do_handshake\\(\\) failed .*?callback failed/,\n]\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 11: lua exception - no yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"lua exception - no yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_certificate_by_lua_block {\n            error(\"bad bad bad\")\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 502\n--- error_log eval\n[\n'runtime error: proxy_ssl_certificate_by_lua(nginx.conf:67):2: bad bad bad',\n'proxy_ssl_certificate_by_lua: handler return value: 500, cert cb exit code: 0',\nqr/.*? SSL_do_handshake\\(\\) failed .*?callback failed/,\n]\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 12: lua exception - yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"lua exception - yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_certificate_by_lua_block {\n            ngx.sleep(0.001)\n            error(\"bad bad bad\")\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 502\n--- error_log eval\n[\n'runtime error: proxy_ssl_certificate_by_lua(nginx.conf:67):3: bad bad bad',\n'proxy_ssl_certificate_by_lua: cert cb exit code: 0',\nqr/.*? SSL_do_handshake\\(\\) failed .*?callback failed/,\n]\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 13: get phase\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"get phase return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_certificate_by_lua_block {\n            print(\"get_phase: \", ngx.get_phase())\n        }\n    }\n--- request\nGET /t\n--- response_body\nget phase return\n--- error_log\nget_phase: proxy_ssl_cert\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 14: subrequests disabled\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"subrequests disabled\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_certificate_by_lua_block {\n            ngx.location.capture(\"/foo\")\n        }\n    }\n--- request\nGET /t\n--- error_code: 502\n--- error_log eval\n[\n'proxy_ssl_certificate_by_lua(nginx.conf:67):2: API disabled in the context of proxy_ssl_certificate_by_lua*',\n'proxy_ssl_certificate_by_lua: handler return value: 500, cert cb exit code: 0',\nqr/.*? SSL_do_handshake\\(\\) failed .*?callback failed/,\n]\n--- no_error_log\n[alert]\n\n\n\n=== TEST 15: simple logging (by_lua_file)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging by lua file\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- user_files\n>>> a.lua\nprint(\"proxy ssl certificate by lua is running!\")\n\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_certificate_by_lua_file html/a.lua;\n    }\n--- request\nGET /t\n--- response_body\nsimple logging by lua file\n--- error_log\na.lua:1: proxy ssl certificate by lua is running!\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 16: coroutine API\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"coroutine API\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_certificate_by_lua_block {\n            local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield\n\n            local function f()\n                local cnt = 0\n                for i = 1, 20 do\n                    print(\"co yield: \", cnt)\n                    cy()\n                    cnt = cnt + 1\n                end\n            end\n\n            local c = cc(f)\n            for i = 1, 3 do\n                print(\"co resume, status: \", coroutine.status(c))\n                cr(c)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\ncoroutine API\n--- grep_error_log eval: qr/co (?:yield: \\d+|resume, status: \\w+)/\n--- grep_error_log_out\nco resume, status: suspended\nco yield: 0\nco resume, status: suspended\nco yield: 1\nco resume, status: suspended\nco yield: 2\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 17: simple user thread wait with yielding\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple user thread wait with yielding\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_certificate_by_lua_block {\n            local function f()\n                ngx.sleep(0.01)\n                print(\"uthread: hello in thread\")\n                return \"done\"\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.log(ngx.ERR, \"uthread: failed to spawn thread: \", err)\n                return ngx.exit(ngx.ERROR)\n            end\n\n            print(\"uthread: thread created: \", coroutine.status(t))\n\n            local ok, res = ngx.thread.wait(t)\n            if not ok then\n                print(\"uthread: failed to wait thread: \", res)\n                return\n            end\n\n            print(\"uthread: \", res)\n        }\n    }\n--- request\nGET /t\n--- response_body\nsimple user thread wait with yielding\n--- no_error_log\n[error]\n[alert]\n--- grep_error_log eval: qr/uthread: [^.,]+/\n--- grep_error_log_out\nuthread: thread created: running while loading proxy ssl certificate by lua\nuthread: hello in thread while loading proxy ssl certificate by lua\nuthread: done while loading proxy ssl certificate by lua\n\n\n\n=== TEST 18: uthread (kill)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"uthread (kill)\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_certificate_by_lua_block {\n            local function f()\n                ngx.log(ngx.INFO, \"uthread: hello from f()\")\n                ngx.sleep(1)\n            end\n\n            local t, err = ngx.thread.spawn(f)\n            if not t then\n                ngx.log(ngx.ERR, \"failed to spawn thread: \", err)\n                return ngx.exit(ngx.ERROR)\n            end\n\n            local ok, res = ngx.thread.kill(t)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to kill thread: \", res)\n                return\n            end\n\n            ngx.log(ngx.INFO, \"uthread: killed\")\n\n            local ok, err = ngx.thread.kill(t)\n            if not ok then\n                ngx.log(ngx.INFO, \"uthread: failed to kill: \", err)\n            end\n        }\n    }\n--- request\nGET /t\n--- response_body\nuthread (kill)\n--- no_error_log\n[error]\n[alert]\n[emerg]\n--- grep_error_log eval: qr/uthread: [^.,]+/\n--- grep_error_log_out\nuthread: hello from f() while loading proxy ssl certificate by lua\nuthread: killed while loading proxy ssl certificate by lua\nuthread: failed to kill: already waited or killed while loading proxy ssl certificate by lua\n\n\n\n=== TEST 19: ngx.exit(ngx.OK) - no yield\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"ngx.exit(ngx.OK) - no yield\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_certificate_by_lua_block {\n            ngx.exit(ngx.OK)\n            ngx.log(ngx.ERR, \"should never reached here...\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nngx.exit(ngx.OK) - no yield\n--- error_log eval\n[\n'proxy_ssl_certificate_by_lua: handler return value: 0, cert cb exit code: 1',\nqr/\\[debug\\] .*? SSL_do_handshake: 1/,\n'lua exit with code 0',\n]\n--- no_error_log\nshould never reached here\n[alert]\n[emerg]\n\n\n\n=== TEST 20: proxy_ssl_certificate_by_lua* without yield API (simple logic)\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"without yield API, simple logic\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_ssl_conf_command        VerifyMode Peer;\n\n        proxy_ssl_certificate_by_lua_block {\n            print(\"proxy ssl certificate: simple test start\")\n\n            -- Simple calculations without yield\n            local sum = 0\n            for i = 1, 10 do\n                sum = sum + i\n            end\n\n            print(\"proxy ssl certificate: calculated sum: \", sum)\n\n            -- String operations\n            local str = \"hello\"\n            str = str .. \" world\"\n            print(\"proxy ssl certificate: concatenated string: \", str)\n\n            -- Table operations\n            local t = {a = 1, b = 2, c = 3}\n            local count = 0\n            for k, v in pairs(t) do\n                count = count + v\n            end\n            print(\"proxy ssl certificate: table sum: \", count)\n\n            print(\"proxy ssl certificate: simple test done\")\n        }\n    }\n--- request\nGET /t\n--- response_body\nwithout yield API, simple logic\n--- grep_error_log eval: qr/(proxy ssl certificate: simple test start|proxy ssl certificate: calculated sum: 55|proxy ssl certificate: concatenated string: hello world|proxy ssl certificate: table sum: 6|proxy ssl certificate: simple test done)/\n--- grep_error_log_out\nproxy ssl certificate: simple test start\nproxy ssl certificate: calculated sum: 55\nproxy ssl certificate: concatenated string: hello world\nproxy ssl certificate: table sum: 6\nproxy ssl certificate: simple test done\n\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 21: ngx.ctx to pass data from downstream phase to upstream phase\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        rewrite_by_lua_block {\n            ngx.ctx.greeting = \"I am from rewrite phase\"\n        }\n\n        proxy_ssl_certificate_by_lua_block {\n            ngx.log(ngx.INFO, \"greeting: \", ngx.ctx.greeting)\n        }\n    }\n--- request\nGET /t\n--- response_body\nsimple logging return\n--- error_log\ngreeting: I am from rewrite phase\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 22: upstream connection aborted\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"hello world\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n        proxy_connect_timeout         100ms;\n\n        proxy_ssl_certificate_by_lua_block {\n            ngx.sleep(0.2)\n        }\n    }\n--- request\nGET /t\n--- error_code: 504\n--- response_body_like: 504 Gateway Time-out\n--- error_log\nupstream timed out (110: Connection timed out) while loading proxy ssl certificate by lua\nproxy_ssl_certificate_by_lua: cert cb aborted\n--- no_error_log\n[alert]\n--- wait: 0.5\n\n\n\n=== TEST 23: cosocket\n--- http_config\n    server {\n        listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1;\n        server_name test.com;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block {\n                ngx.sleep(0.1)\n\n                ngx.status = 201\n                ngx.say(\"foo\")\n                ngx.exit(201)\n            }\n            more_clear_headers Date;\n        }\n    }\n\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_certificate_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_RAND_PORT_1)\n                if not ok then\n                    ngx.log(ngx.ERR, \"failed to connect: \", err)\n                    return\n                end\n\n                ngx.log(ngx.INFO, \"connected: \", ok)\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.log(ngx.ERR, \"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.log(ngx.INFO, \"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.log(ngx.ERR, \"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.log(ngx.INFO, \"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.log(ngx.INFO, \"close: \", ok, \" \", err)\n            end -- do\n            -- collectgarbage()\n        }\n    }\n--- request\nGET /t\n--- response_body\nsimple logging return\n--- error_log\nconnected: 1\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived:\nreceived: foo\nclose: 1 nil\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 24: TLSv1.2, without proxy_ssl_certificate, lua does not set cert\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_protocols TLSv1.2;\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_protocols           TLSv1.2;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_certificate_by_lua_block {\n            local proxy_ssl = require \"ngx.proxyssl\"\n\n            local ver, err = proxy_ssl.get_tls1_version_str()\n            if not ver then\n                ngx.log(ngx.ERR, \"failed to get TLS1 version: \", err)\n                return\n            end\n            ngx.log(ngx.INFO, \"got TLS1 version: \", ver)\n        }\n    }\n--- request\nGET /t\n--- error_code: 400\n--- response_body_like: 400 No required SSL certificate was sent\n--- error_log\ngot TLS1 version: TLSv1.2\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 25: TLSv1.2, without proxy_ssl_certificate, lua sets cert\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_protocols TLSv1.2;\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_protocols           TLSv1.2;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_certificate_by_lua_block {\n            local ssl = require \"ngx.ssl\"\n            local proxy_ssl = require \"ngx.proxyssl\"\n            local proxy_ssl_cert = require \"ngx.ssl.proxysslcert\"\n\n            local ver, err = proxy_ssl.get_tls1_version_str()\n            if not ver then\n                ngx.log(ngx.ERR, \"failed to get TLS1 version: \", err)\n                return\n            end\n            ngx.log(ngx.INFO, \"got TLS1 version: \", ver)\n\n            local f = assert(io.open(\"t/cert/mtls_client.crt\"))\n            local cert_data = f:read(\"*a\")\n            f:close()\n\n            local cert, err = ssl.parse_pem_cert(cert_data)\n            if not cert then\n                ngx.log(ngx.ERR, \"failed to parse pem cert: \", err)\n                return\n            end\n\n            local ok, err = proxy_ssl_cert.set_cert(cert)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set cert: \", err)\n                return\n            end\n\n            local f = assert(io.open(\"t/cert/mtls_client.key\"))\n            local pkey_data = f:read(\"*a\")\n            f:close()\n\n            local pkey, err = ssl.parse_pem_priv_key(pkey_data)\n            if not pkey then\n                ngx.log(ngx.ERR, \"failed to parse pem key: \", err)\n                return\n            end\n\n            local ok, err = proxy_ssl_cert.set_priv_key(pkey)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set private key: \", err)\n                return\n            end\n        }\n    }\n--- request\nGET /t\n--- error_code: 200\n--- response_body\nsimple logging return\n--- error_log\ngot TLS1 version: TLSv1.2\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 26: TLSv1.3, without proxy_ssl_certificate, lua does not set cert\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_protocols TLSv1.3;\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_protocols           TLSv1.3;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_certificate_by_lua_block {\n            local proxy_ssl = require \"ngx.proxyssl\"\n\n            local ver, err = proxy_ssl.get_tls1_version_str()\n            if not ver then\n                ngx.log(ngx.ERR, \"failed to get TLS1 version: \", err)\n                return\n            end\n            ngx.log(ngx.INFO, \"got TLS1 version: \", ver)\n        }\n    }\n--- request\nGET /t\n--- error_code: 400\n--- response_body_like: 400 No required SSL certificate was sent\n--- error_log\ngot TLS1 version: TLSv1.3\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 27: TLSv1.3, without proxy_ssl_certificate, lua sets cert\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_protocols TLSv1.3;\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_protocols           TLSv1.3;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_certificate_by_lua_block {\n            local ssl = require \"ngx.ssl\"\n            local proxy_ssl = require \"ngx.proxyssl\"\n            local proxy_ssl_cert = require \"ngx.ssl.proxysslcert\"\n\n            local ver, err = proxy_ssl.get_tls1_version_str()\n            if not ver then\n                ngx.log(ngx.ERR, \"failed to get TLS1 version: \", err)\n                return\n            end\n            ngx.log(ngx.INFO, \"got TLS1 version: \", ver)\n\n            local f = assert(io.open(\"t/cert/mtls_client.crt\"))\n            local cert_data = f:read(\"*a\")\n            f:close()\n\n            local cert, err = ssl.parse_pem_cert(cert_data)\n            if not cert then\n                ngx.log(ngx.ERR, \"failed to parse pem cert: \", err)\n                return\n            end\n\n            local ok, err = proxy_ssl_cert.set_cert(cert)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set cert: \", err)\n                return\n            end\n\n            local f = assert(io.open(\"t/cert/mtls_client.key\"))\n            local pkey_data = f:read(\"*a\")\n            f:close()\n\n            local pkey, err = ssl.parse_pem_priv_key(pkey_data)\n            if not pkey then\n                ngx.log(ngx.ERR, \"failed to parse pem key: \", err)\n                return\n            end\n\n            local ok, err = proxy_ssl_cert.set_priv_key(pkey)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set private key: \", err)\n                return\n            end\n        }\n    }\n--- request\nGET /t\n--- error_code: 200\n--- response_body\nsimple logging return\n--- error_log\ngot TLS1 version: TLSv1.3\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 28: TLSv1.2, with proxy_ssl_certificate, lua does not set cert\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_protocols TLSv1.2;\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_protocols           TLSv1.2;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_certificate_by_lua_block {\n            local proxy_ssl = require \"ngx.proxyssl\"\n\n            local ver, err = proxy_ssl.get_tls1_version_str()\n            if not ver then\n                ngx.log(ngx.ERR, \"failed to get TLS1 version: \", err)\n                return\n            end\n            ngx.log(ngx.INFO, \"got TLS1 version: \", ver)\n        }\n    }\n--- request\nGET /t\n--- error_code: 200\n--- response_body\nsimple logging return\n--- error_log\ngot TLS1 version: TLSv1.2\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 29: TLSv1.3, with proxy_ssl_certificate, lua does not set cert\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_protocols TLSv1.3;\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_protocols           TLSv1.3;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_certificate_by_lua_block {\n            local proxy_ssl = require \"ngx.proxyssl\"\n\n            local ver, err = proxy_ssl.get_tls1_version_str()\n            if not ver then\n                ngx.log(ngx.ERR, \"failed to get TLS1 version: \", err)\n                return\n            end\n            ngx.log(ngx.INFO, \"got TLS1 version: \", ver)\n        }\n    }\n--- request\nGET /t\n--- error_code: 200\n--- response_body\nsimple logging return\n--- error_log\ngot TLS1 version: TLSv1.3\n--- no_error_log\n[error]\n[alert]\n\n\n\n=== TEST 30: proxy_ssl_certificate_by_lua takes precedence over proxy_ssl_certificate\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        server_name   test.com;\n\n        ssl_protocols TLSv1.3;\n        ssl_verify_client on;\n        ssl_certificate ../../cert/mtls_server.crt;\n        ssl_certificate_key ../../cert/mtls_server.key;\n        ssl_client_certificate ../../cert/mtls_ca.crt;\n\n        location / {\n            default_type 'text/plain';\n\n            content_by_lua_block {\n                ngx.say(\"simple logging return\")\n            }\n\n            more_clear_headers Date;\n        }\n    }\n--- config\n    location /t {\n        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;\n        proxy_ssl_protocols           TLSv1.3;\n        proxy_ssl_verify              on;\n        proxy_ssl_name                example.com;\n        proxy_ssl_certificate         ../../cert/mtls_client.crt;\n        proxy_ssl_certificate_key     ../../cert/mtls_client.key;\n        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;\n        proxy_ssl_session_reuse       off;\n\n        proxy_ssl_certificate_by_lua_block {\n            local ssl = require \"ngx.ssl\"\n            local proxy_ssl = require \"ngx.proxyssl\"\n            local proxy_ssl_cert = require \"ngx.ssl.proxysslcert\"\n\n            local ver, err = proxy_ssl.get_tls1_version_str()\n            if not ver then\n                ngx.log(ngx.ERR, \"failed to get TLS1 version: \", err)\n                return\n            end\n            ngx.log(ngx.INFO, \"got TLS1 version: \", ver)\n\n            -- there exists proxy_ssl_certificate and proxy_ssl_certificate_key\n            -- directives in nginx conf, but here we use lua codes to clear them,\n            -- so that it can prove that proxy_ssl_certificate_by_lua takes\n            -- precedence over proxy_ssl_certificate related directives\n            proxy_ssl_cert.clear_certs()\n        }\n    }\n--- request\nGET /t\n--- error_code: 400\n--- response_body_like: 400 No required SSL certificate was sent\n--- error_log\ngot TLS1 version: TLSv1.3\n--- no_error_log\n[error]\n[alert]\n"
  },
  {
    "path": "t/170-ssl-session-reuse.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Cwd qw(abs_path realpath);\nuse File::Basename;\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nsub resolve($$);\n\nplan tests => repeat_each() * (blocks() * 2);\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n$ENV{TEST_NGINX_SERVER_SSL_PORT} ||= 12345;\n$ENV{TEST_NGINX_CERT_DIR} ||= dirname(realpath(abs_path(__FILE__)));\n$ENV{TEST_NGINX_OPENRESTY_ORG_IP} ||= resolve(\"openresty.org\", $ENV{TEST_NGINX_RESOLVER});\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\n\nsub read_file {\n    my $infile = shift;\n    open my $in, $infile\n        or die \"cannot open $infile for reading: $!\";\n    my $cert = do { local $/; <$in> };\n    close $in;\n    $cert;\n}\n\nsub resolve ($$) {\n    my ($domain, $resolver) = @_;\n    my $ips = qx/dig \\@$resolver +short $domain/;\n\n    my $exit_code = $? >> 8;\n    if (!$ips || $exit_code != 0) {\n        die \"failed to resolve '$domain' using '$resolver' as resolver\";\n    }\n\n    my ($ip) = split /\\n/, $ips;\n    return $ip;\n}\n\nour $DSTRootCertificate = read_file(\"t/cert/dst-ca.crt\");\nour $EquifaxRootCertificate = read_file(\"t/cert/equifax.crt\");\nour $TestCertificate = read_file(\"t/cert/test.crt\");\nour $TestCertificateKey = read_file(\"t/cert/test.key\");\nour $TestCRL = read_file(\"t/cert/test.crl\");\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: www.google.com\naccess the public network is unstable, need a bigger timeout value.\n--- quic_max_idle_timeout: 3\n--- config\n    server_tokens off;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(2000)\n            local ok, err = sock:connect(\"www.google.com\", 443)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local sess, err = sock:sslhandshake()\n            if not sess then\n                ngx.say(\"failed to do SSL handshake: \", err)\n                return\n            end\n\n            ngx.say(\"ssl handshake: \", type(sess))\n\n            local req = \"GET / HTTP/1.1\\r\\nHost: www.google.com\\r\\nConnection: close\\r\\n\\r\\n\"\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send http request: \", err)\n                return\n            end\n\n            ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n            local line, err = sock:receive()\n            if not line then\n                ngx.say(\"failed to receive response status line: \", err)\n                return\n            end\n\n            ngx.say(\"received: \", line)\n\n            local sess, err = sock:getsslsession()\n            if not sess then\n                ngx.say(\"failed to get SSL session: \", err)\n            else\n                ngx.say(\"ssl session: \", type(sess))\n            end\n\n            local ok, err = sock:close()\n            ngx.say(\"close: \", ok, \" \", err)\n        }\n    }\n\n--- request\nGET /t\n--- response_body_like chop\n\\Aconnected: 1\nssl handshake: cdata\nsent http request: 59 bytes.\nreceived: HTTP/1.1 (?:200 OK|302 Found)\n(?:ssl session: cdata|failed to get SSL session: not resumable)\nclose: 1 nil\n\\z\n--- timeout: 5\n\n\n\n=== TEST 2: connect to nginx server\n--- http_config\n    server {\n        listen $TEST_NGINX_SERVER_SSL_PORT ssl;\n        server_name   test.com;\n        ssl_certificate ../html/test.crt;\n        ssl_certificate_key ../html/test.key;\n\n        location / {\n            content_by_lua_block {\n                ngx.exit(201)\n            }\n        }\n    }\n--- config\n    # fixme: getsslsession doesn't work with BoringSSL TLSv1.3 case, temporarily disable TLSv1.3 for this test.\n    lua_ssl_protocols TLSv1.2;\n    resolver $TEST_NGINX_RESOLVER ipv6=off;\n    location /t {\n        content_by_lua_block {\n            local ssl_session\n\n            local function http_req()\n                local sock = ngx.socket.tcp()\n                sock:settimeout(2000)\n                local ok, err = sock:connect(\"127.0.0.1\", $TEST_NGINX_SERVER_SSL_PORT)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                ssl_session, err = sock:sslhandshake(ssl_session)\n                if not ssl_session then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(ssl_session))\n                ngx.say(\"ssl handshake: \", tostring(ssl_session))\n\n                local req = \"GET / HTTP/1.1\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                local line, err = sock:receive()\n                if not line then\n                    ngx.say(\"failed to receive response status line: \", err)\n                    return\n                end\n\n                ngx.say(\"received: \", line)\n\n                ssl_session, err = sock:getsslsession()\n                if ssl_session == nil then\n                    ngx.say(\"failed to get SSL session: \", err)\n                end\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end\n\n            http_req()\n            http_req()\n        }\n    }\n\n--- request\nGET /t\n--- response_body eval\nqr/connected: 1\nssl handshake: cdata\nssl handshake: cdata<void \\*>: 0x[0-9a-f]+\nsent http request: 53 bytes.\nreceived: HTTP\\/1.1 201 Created\nclose: 1 nil\nconnected: 1\nssl handshake: cdata\nssl handshake: cdata<void \\*>: 0x[0-9a-f]+\nsent http request: 53 bytes.\nreceived: HTTP\\/1.1 201 Created\nclose: 1 nil\n/\n--- user_files eval\n\">>> test.key\n$::TestCertificateKey\n>>> test.crt\n$::TestCertificate\"\n"
  },
  {
    "path": "t/185-ngx-buf-double-free.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket 'no_plan';\n\nrepeat_each(2);\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: one buf was linked to multiple ngx_chain_t nodes\n--- config\n    location /t {\n        content_by_lua_block {\n            local str = string.rep(\".\", 1300)\n            ngx.print(str)\n            ngx.flush()\n            ngx.print(\"small chunk\")\n            ngx.flush()\n        }\n        body_filter_by_lua_block {local dummy=1}\n    }\n--- request\nGET /t\n--- response_body_like: small chunk\n"
  },
  {
    "path": "t/186-cosocket-busy-bufs.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\n\nuse Test::Nginx::Socket;\nuse Test::Nginx::Socket::Lua::Stream;\n\nlog_level('warn');\nrepeat_each(2);\n\nif (defined $ENV{TEST_NGINX_USE_HTTP3}) {\n    plan(skip_all => \"HTTP3 does not support client abort\");\n} elsif (defined $ENV{TEST_NGINX_USE_HTTP2}) {\n    plan(skip_all => \"HTTP2 does not support client abort\");\n} else {\n    plan tests => repeat_each() * (blocks() * 2);\n}\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: ngx.say and cosocket\n--- stream_server_config\n    content_by_lua_block {\n        local sock = assert(ngx.req.socket(true))\n        sock:settimeout(1000)\n        while true do\n            local data = sock:receive(5)\n            if not data then\n                return\n            end\n            ngx.print(data)\n            ngx.flush(true)\n        end\n    }\n--- config\n    location /test {\n        content_by_lua_block {\n            ngx.say(\"hello\")\n            --ngx.flush(true)\n\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", ngx.var.server_port + 1)\n            assert(ok)\n\n            local last_duration = 0\n            local cnt = 0\n            local t1, t2\n            local err_cnt = 0\n            local ERR_THRESHOLD_MS = 100\n\n            for i = 1,100000 do\n                if cnt == 0 then\n                    ngx.update_time()\n                    t1 = ngx.now()\n                end\n\n                cnt = cnt + 1\n\n                local sent = sock:send(\"hello\")\n                local data = sock:receive(5)\n                assert(data==\"hello\")\n\n                if cnt == 1000 then\n                    cnt = 0\n                    ngx.update_time()\n                    t2 = ngx.now()\n                    local duration = (t2 - t1) * 1000\n                    if last_duration > 0 and (duration - last_duration) > ERR_THRESHOLD_MS then\n                        if err_cnt >= 3 then\n                            ngx.log(ngx.ERR,\n                                \"more than \", err_cnt, \" times, duration larger than \",\n                                ERR_THRESHOLD_MS, \" ms, \",\n                                \"last_duration: \", math.floor(duration), \" ms\")\n                            return ngx.exit(500)\n                        end\n                        err_cnt = err_cnt + 1\n                    end\n                    last_duration = duration\n                end\n            end\n\n            sock:close()\n            ngx.exit(200)\n        }\n    }\n--- no_error_log\n[error]\n--- timeout: 30\n--- request\nGET /test\n"
  },
  {
    "path": "t/187-ssl-two-verification.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(3);\n\n# All these tests need to have new openssl\nmy $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';\nmy $openssl_version = eval { `$NginxBinary -V 2>&1` };\n\nif ($openssl_version =~ m/built with OpenSSL (0\\S*|1\\.0\\S*|1\\.1\\.0\\S*)/) {\n    plan(skip_all => \"too old OpenSSL, need >= 1.1.1, was $1\");\n} elsif ($openssl_version =~ m/running with BoringSSL/) {\n    plan(skip_all => \"does not support BoringSSL\");\n} else {\n    plan tests => repeat_each() * (blocks() * 7);\n}\n\n$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: simple logging\n--- http_config\n    server {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;\n        #listen 127.0.0.1:4433 ssl;\n        server_name   test.com;\n        ssl_client_hello_by_lua_block { print(\"ssl client hello by lua is running!\") }\n        ssl_certificate ../../cert/test.crt;\n        ssl_certificate_key ../../cert/test.key;\n        #ssl_trusted_certificate ../../cert/test.crt;\n        ssl_client_certificate ../../cert/test.crt;\n        ssl_verify_client on;\n        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;\n\n        server_tokens off;\n        location /foo {\n            default_type 'text/plain';\n            content_by_lua_block { ngx.status = 201 ngx.say(\"foo\") ngx.exit(201) }\n            log_by_lua_block {\n                ngx.log(ngx.INFO, \"ssl_client_s_dn: \", ngx.var.ssl_client_s_dn)\n            }\n            more_clear_headers Date;\n        }\n    }\n--- config\n    server_tokens off;\n    lua_ssl_certificate ../../cert/test.crt;\n    lua_ssl_certificate_key ../../cert/test.key;\n    lua_ssl_trusted_certificate ../../cert/test.crt;\n\n    location /t {\n        content_by_lua_block {\n            do\n                local sock = ngx.socket.tcp()\n\n                sock:settimeout(2000)\n\n                local ok, err = sock:connect(\"unix:$TEST_NGINX_HTML_DIR/nginx.sock\")\n                -- local ok, err = sock:connect(\"127.0.0.1\", 4433)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                ngx.say(\"connected: \", ok)\n\n                local sess, err = sock:sslhandshake(nil, \"test.com\", true)\n                if not sess then\n                    ngx.say(\"failed to do SSL handshake: \", err)\n                    return\n                end\n\n                ngx.say(\"ssl handshake: \", type(sess))\n\n                local req = \"GET /foo HTTP/1.0\\r\\nHost: test.com\\r\\nConnection: close\\r\\n\\r\\n\"\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send http request: \", err)\n                    return\n                end\n\n                ngx.say(\"sent http request: \", bytes, \" bytes.\")\n\n                while true do\n                    local line, err = sock:receive()\n                    if not line then\n                        -- ngx.say(\"failed to receive response status line: \", err)\n                        break\n                    end\n\n                    ngx.say(\"received: \", line)\n                end\n\n                local ok, err = sock:close()\n                ngx.say(\"close: \", ok, \" \", err)\n            end  -- do\n            -- collectgarbage()\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nconnected: 1\nssl handshake: cdata\nsent http request: 56 bytes.\nreceived: HTTP/1.1 201 Created\nreceived: Server: nginx\nreceived: Content-Type: text/plain\nreceived: Content-Length: 4\nreceived: Connection: close\nreceived: \nreceived: foo\nclose: 1 nil\n\n--- error_log\nlua ssl server name: \"test.com\"\nssl_client_s_dn: emailAddress=agentzh@gmail.com,CN=test.com,OU=OpenResty,O=OpenResty,L=San Francisco,ST=California,C=US\n\n--- no_error_log\n[error]\n[alert]\n--- grep_error_log eval: qr/ssl_client_hello_by_lua\\(.*?,|\\bssl client hello: connection reusable: \\d+|\\breusable connection: \\d+/\n--- grep_error_log_out eval\n# Since nginx version 1.17.9, nginx call ngx_reusable_connection(c, 0)\n# before call ssl callback function\n$Test::Nginx::Util::NginxVersion >= 1.017009 ?\nqr/reusable connection: 0\nssl client hello: connection reusable: 0\nssl_client_hello_by_lua\\(nginx.conf:\\d+\\):1: ssl client hello by lua is running!,/\n: qr /reusable connection: 1\nssl client hello: connection reusable: 1\nreusable connection: 0\nssl_client_hello_by_lua\\(nginx.conf:\\d+\\):1: ssl client hello by lua is running!,/\n"
  },
  {
    "path": "t/188-balancer_keepalive_pool_max_retry.t",
    "content": "# vim:set ft= ts=4 sw=4 et:\n\nuse Test::Nginx::Socket::Lua;\nuse Cwd qw(cwd);\n\nlog_level('info');\nrepeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 6);\n\nmy $pwd = cwd();\n\nno_long_string();\n\ncheck_accum_error_log();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- http_config\n    lua_shared_dict request_counter 1m;\n    upstream my_upstream {\n        server 127.0.0.1;\n        balancer_by_lua_block {\n            local balancer = require \"ngx.balancer\"\n\n            if not ngx.ctx.tries then\n                ngx.ctx.tries = 0\n            end\n\n            ngx.ctx.tries = ngx.ctx.tries + 1\n            ngx.log(ngx.INFO, \"tries \", ngx.ctx.tries)\n\n            if ngx.ctx.tries == 1 then\n                balancer.set_more_tries(5)\n            end\n\n            local host = \"127.0.0.1\"\n            local port = $TEST_NGINX_RAND_PORT_1;\n\n            local ok, err = balancer.set_current_peer(host, port)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to set the current peer: \", err)\n                return ngx.exit(500)\n            end\n\n            balancer.set_timeouts(60000, 60000, 60000)\n\n            local ok, err = balancer.enable_keepalive(60, 100)\n            if not ok then\n                ngx.log(ngx.ERR, \"failed to enable keepalive: \", err)\n                return ngx.exit(500)\n            end\n        }\n    }\n\n    server {\n        listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1;\n        location /hello {\n            content_by_lua_block{\n                local request_counter = ngx.shared.request_counter\n                local first_request = request_counter:get(\"first_request\")\n                if first_request == nil then\n                    request_counter:set(\"first_request\", \"yes\")\n                    ngx.print(\"hello\")\n                else\n                    ngx.exit(ngx.HTTP_CLOSE)\n                end\n            }\n        }\n    }\n--- config\n    location = /t {\n        proxy_pass http://my_upstream;\n        proxy_set_header Connection \"keep-alive\";\n\n        rewrite_by_lua_block {\n           ngx.req.set_uri(\"/hello\")\n        }\n    }\n--- pipelined_requests eval\n[\"GET /t HTTP/1.1\" , \"GET /t HTTP/1.1\"]\n--- response_body eval\n[\"hello\", qr/502/]\n--- error_code eval\n[200, 502]\n--- no_error_log eval\nqr/tries 7/\n"
  },
  {
    "path": "t/189-precontent.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\nuse t::StapThread;\n\nour $GCScript = <<_EOC_;\n$t::StapThread::GCScript\n\nF(ngx_http_lua_check_broken_connection) {\n    println(\"lua check broken conn\")\n}\n\nF(ngx_http_lua_request_cleanup) {\n    println(\"lua req cleanup\")\n}\n_EOC_\n\nour $StapScript = $t::StapThread::StapScript;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 + 6);\n\n#log_level(\"info\");\n#no_long_string();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: precontent_by_lua_block basic test\n--- config\n    location /lua {\n        precontent_by_lua_block {\n            ngx.log(ngx.INFO, \"precontent_by_lua_block executed\")\n        }\n        content_by_lua_block {\n            ngx.say(\"content phase executed\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\ncontent phase executed\n--- error_log\nprecontent_by_lua_block executed\n--- no_error_log\n[error]\n\n\n\n=== TEST 2: precontent_by_lua_block with ngx.exit(ngx.OK)\n--- config\n    location /lua {\n        precontent_by_lua_block {\n            ngx.log(ngx.INFO, \"precontent phase executed\")\n            ngx.exit(ngx.OK)\n        }\n        content_by_lua_block {\n            ngx.say(\"content phase executed\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\ncontent phase executed\n--- error_log\nprecontent phase executed\n--- no_error_log\n[error]\n\n\n\n=== TEST 3: precontent_by_lua_block with ngx.exit(403)\n--- config\n    location /lua {\n        precontent_by_lua_block {\n            ngx.exit(403)\n        }\n        content_by_lua_block {\n            ngx.say(\"content phase executed\")\n        }\n    }\n--- request\nGET /lua\n--- error_code: 403\n--- no_error_log\n[error]\n\n\n\n=== TEST 4: precontent_by_lua_block accessing request body\n--- config\n    location /test {\n        precontent_by_lua_block {\n            ngx.req.read_body()\n            local body = ngx.req.get_body_data()\n            if body then\n                ngx.log(ngx.INFO, \"precontent got body: \" .. body)\n            end\n        }\n        content_by_lua_block {\n            ngx.say(\"content phase\")\n        }\n    }\n--- request\nPOST /test\nhello world\n--- response_body\ncontent phase\n--- error_log\nprecontent got body: hello world\n--- no_error_log\n[error]\n\n\n\n=== TEST 5: precontent_by_lua_block setting ctx for content phase\n--- config\n    location /lua {\n        precontent_by_lua_block {\n            ngx.ctx.precontent = \"set in precontent\"\n        }\n        content_by_lua_block {\n            ngx.say(\"var from precontent: \", ngx.ctx.precontent or \"nil\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\nvar from precontent: set in precontent\n--- no_error_log\n[error]\n\n\n\n=== TEST 6: precontent_by_lua_file\n--- config\n    location /lua {\n        precontent_by_lua_file html/test.lua;\n        content_by_lua_block {\n            ngx.say(\"content phase\")\n        }\n    }\n--- user_files\n>>> test.lua\nngx.log(ngx.INFO, \"precontent_by_lua_file executed\")\n--- request\nGET /lua\n--- response_body\ncontent phase\n--- error_log\nprecontent_by_lua_file executed\n--- no_error_log\n[error]\n\n\n\n=== TEST 7: precontent_by_lua_block with subrequests\n--- config\n    location /main {\n        precontent_by_lua_block {\n            ngx.log(ngx.INFO, \"precontent phase - is_subrequest: \", ngx.is_subrequest)\n        }\n        content_by_lua_block {\n            local res = ngx.location.capture(\"/sub\")\n            ngx.print(res.body)\n        }\n    }\n    \n    location /sub {\n        content_by_lua_block {\n            ngx.say(\"subrequest executed\")\n        }\n    }\n--- request\nGET /main\n--- response_body\nsubrequest executed\n--- error_log\nprecontent phase - is_subrequest: false\n--- no_error_log\n[error]\n\n\n\n=== TEST 8: precontent_by_lua_block getting phase name\n--- config\n    location /phase {\n        precontent_by_lua_block {\n            ngx.log(ngx.INFO, \"current phase: \", ngx.get_phase())\n        }\n        content_by_lua_block {\n            ngx.say(\"content phase\")\n        }\n    }\n--- request\nGET /phase\n--- response_body\ncontent phase\n--- error_log\ncurrent phase: precontent\n--- no_error_log\n[error]\n\n\n\n=== TEST 9: precontent_by_lua_block with header manipulation\n--- config\n    location /header {\n        precontent_by_lua_block {\n            ngx.req.set_header(\"X-Precontent\", \"added-in-precontent\")\n        }\n        content_by_lua_block {\n            ngx.say(\"Header X-Precontent: \", ngx.var.http_x_precontent or \"not found\")\n        }\n    }\n--- request\nGET /header\n--- response_body\nHeader X-Precontent: added-in-precontent\n--- no_error_log\n[error]\n\n\n\n=== TEST 10: rewrite args\n--- config\n    location /args {\n        precontent_by_lua_block {\n            ngx.req.set_uri_args(\"modified=1&test=2\")\n        }\n        content_by_lua_block {\n            ngx.say(\"Args in content: \", ngx.var.args)\n        }\n    }\n--- request\nGET /args?original=1\n--- response_body\nArgs in content: modified=1&test=2\n--- no_error_log\n[error]\n\n\n\n=== TEST 11: syntax error in precontent_by_lua_block\n--- config\n    location /lua {\n\n        precontent_by_lua_block {\n            'for end';\n        }\n        content_by_lua_block {\n            ngx.say(\"Hello world\")\n        }\n    }\n--- request\nGET /lua\n--- ignore_response\n--- error_log\nfailed to load inlined Lua code: precontent_by_lua(nginx.conf:41):2: unexpected symbol near ''for end''\n--- no_error_log\nno_such_error\n--- skip_eval: 2:$ENV{TEST_NGINX_USE_HUP}\n\n\n\n=== TEST 12: precontent_by_lua_block directive in server\n--- config\n    precontent_by_lua_block {\n        ngx.ctx.server_level_precontent = \"executed\"\n    }\n    location /inherit {\n        content_by_lua_block {\n            ngx.say(\"Server level precontent ran: \", ngx.ctx.server_level_precontent)\n        }\n    }\n--- request\nGET /inherit\n--- response_body\nServer level precontent ran: executed\n--- no_error_log\n[error]\n\n\n\n=== TEST 13: precontent_by_lua_block overriding in location\n--- config\n    precontent_by_lua_block {\n        ngx.ctx.server_level_precontent = \"server_default\"\n    }\n    location /override {\n        precontent_by_lua_block {\n            ngx.ctx.location_level_precontent = \"location_specific\"\n        }\n        content_by_lua_block {\n            ngx.say(\"Server level: \", ngx.ctx.server_level_precontent or \"not executed\")\n            ngx.say(\"Location level: \", ngx.ctx.location_level_precontent or \"not executed\")\n        }\n    }\n--- request\nGET /override\n--- response_body\nServer level: not executed\nLocation level: location_specific\n--- no_error_log\n[error]\n\n\n\n=== TEST 14: sleep\n--- config\n    location /lua {\n        precontent_by_lua_block {\n            ngx.sleep(0.001)\n            ngx.log(ngx.INFO, \"precontent_by_lua_block executed\")\n        }\n        content_by_lua_block {\n            ngx.say(\"content phase executed\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\ncontent phase executed\n--- error_log\nprecontent_by_lua_block executed\n--- no_error_log\n[error]\n\n\n\n=== TEST 15: cosocket\n--- config\n    location /lua {\n        precontent_by_lua_block {\n            local sock, err = ngx.socket.tcp()\n            if not sock then\n                ngx.log(ngx.ERR, \"Failed to create sock: \", err)\n                return\n            end\n\n            local ok\n            ok, err = sock:connect(\"127.0.0.1\", ngx.var.server_port)\n            if not ok then\n                ngx.log(ngx.ERR, \"Failed to connect to google: \", err)\n            end\n            sock:close()\n\n            ngx.log(ngx.INFO, \"precontent_by_lua_block executed\")\n        }\n        content_by_lua_block {\n            ngx.say(\"content phase executed\")\n        }\n    }\n--- request\nGET /lua\n--- response_body\ncontent phase executed\n--- error_log\nprecontent_by_lua_block executed\n--- no_error_log\n[error]\n"
  },
  {
    "path": "t/191-pipe-proc-quic-close-crash.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n#\n# Regression test for use-after-free crash in ngx_http_lua_pipe_resume_read_stdout_handler.\n#\n# Root cause: pool cleanup runs in LIFO order.  pipe_proc_destroy (registered\n# when the pipe is spawned, i.e. *later*) therefore runs *before*\n# request_cleanup_handler (registered at Lua handler init time).\n# pipe_proc_destroy calls pipe_proc_finalize → ngx_http_lua_pipe_close_helper,\n# which, when a read is in progress, posts the event rather than calling\n# ngx_close_connection, leaving the read-timeout timer live.  It then sets\n# proc->pipe = NULL.  When request_cleanup_handler runs next it sees\n# proc->pipe == NULL and returns early, so the timer is never cancelled.\n# After ngx_destroy_pool(r->pool) frees wait_co_ctx, the timer fires and\n# dereferences the dangling pointer → SIGSEGV.\n#\n# Trigger path (QUIC-specific):\n#   QUIC connection close\n#   → ngx_quic_close_streams → sc->read->handler (no-op without lua_check_client_abort)\n#   → ngx_http_free_request → ngx_destroy_pool(r->pool)\n#   → LIFO pool cleanups: pipe_proc_destroy first, request_cleanup_handler second\n#   → timer remains live; pool freed; timer fires → SIGSEGV\n#\n# Fix (ngx_http_lua_ffi_pipe_proc_destroy): after pipe_proc_finalize, call\n# ngx_close_connection on any pipe ctx whose connection is still open.\n# ngx_close_connection removes both the read-timeout timer (ngx_del_timer)\n# and any already-posted event (ngx_delete_posted_event), preventing the UAF.\n#\n# Timing:\n#   pipe stdout read timeout : 1 s  (timer armed 1 s after request lands)\n#   curl --max-time           : 0.5 s  (QUIC connection closed while timer live)\n#   --- wait                  : 2 s  (covers remaining ~0.5 s + safety margin)\n\n\nBEGIN {\n    if (!$ENV{TEST_NGINX_USE_HTTP3}) {\n        $SkipReason = \"TEST_NGINX_USE_HTTP3 is not set\";\n    } else {\n        my $nginx = $ENV{TEST_NGINX_BINARY} || 'nginx';\n        my $v     = eval { `$nginx -V 2>&1` } // '';\n\n        if ($v !~ /--with-http_v3_module/) {\n            $SkipReason = \"requires nginx built with --with-http_v3_module\";\n        }\n    }\n}\n\nuse Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();\n\nmaster_on();   # master process needed to detect/survive worker crash\nworkers(1);\n\nrepeat_each(1);\n\n# 1 subtest per block: the --- no_error_log [alert] check.\n# (--- ignore_response suppresses the default status-code subtest.)\nplan tests => repeat_each() * blocks();\n\nlog_level('info');\nno_long_string();\n\nadd_block_preprocessor(sub {\n    my $block = shift;\n\n    my $http_config = $block->http_config || '';\n    $http_config .= <<'_EOC_';\n    lua_package_path \"../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;\";\n\n    init_by_lua_block {\n        require \"resty.core\"\n    }\n_EOC_\n    $block->set_value(\"http_config\", $http_config);\n});\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: pipe read timer must not fire after pool is freed on QUIC connection close\n--- config\n    location = /t {\n        content_by_lua_block {\n            -- Spawn a long-lived child; stdout will never produce output.\n            -- set_timeouts(write_timeout, stdout_timeout, stderr_timeout, wait_timeout)\n            local proc = require(\"ngx.pipe\").spawn({\"sleep\", \"100\"})\n            proc:set_timeouts(nil, 1000)  -- 1 s stdout read timeout\n\n            -- This call yields and arms a 1 s timer.\n            -- The test client closes the QUIC connection before the timer fires,\n            -- triggering the LIFO pool-cleanup use-after-free (without the fix).\n            proc:stdout_read_line()\n        }\n    }\n--- request\nGET /t\n--- timeout: 0.5\n--- wait: 2\n--- ignore_response\n--- curl_error eval: qr/\\(28\\)/\n--- no_error_log\n[alert]\n"
  },
  {
    "path": "t/302-tcp-socket-timeout-log.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nBEGIN {\n    if (!defined $ENV{LD_PRELOAD}) {\n        $ENV{LD_PRELOAD} = '';\n    }\n\n    if ($ENV{LD_PRELOAD} !~ /\\bmockeagain\\.so\\b/) {\n        $ENV{LD_PRELOAD} = \"mockeagain.so $ENV{LD_PRELOAD}\";\n    }\n\n    if ($ENV{MOCKEAGAIN} eq 'r') {\n        $ENV{MOCKEAGAIN} = 'rw';\n\n    } else {\n        $ENV{MOCKEAGAIN} = 'w';\n    }\n\n    $ENV{TEST_NGINX_EVENT_TYPE} = 'poll';\n    delete($ENV{TEST_NGINX_USE_HTTP2});\n    $ENV{MOCKEAGAIN_WRITE_TIMEOUT_PATTERN} = 'slowdata';\n}\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nif (defined $ENV{TEST_NGINX_SKIP_COSOCKET_LOG_TEST}) {\n    plan(skip_all => \"Remove TEST_NGINX_SKIP_COSOCKET_LOG_TEST to enable this test\");\n} else {\n    plan tests => repeat_each() * (blocks() * 3);\n}\n\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\n#log_level 'warn';\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: read timeout\n--- config\n    server_tokens off;\n    location /t {\n        resolver $TEST_NGINX_RESOLVER ipv6=off;\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            sock:settimeouts(150, 150, 2)  -- 2ms read timeout\n\n            local port = ngx.var.server_port\n            local ok, err = sock:connect(\"localhost\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local req = \"GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n\n    location /foo {\n        content_by_lua_block {\n            ngx.sleep(0.01) -- 10 ms\n            ngx.say(\"foo\")\n        }\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body_like\nfailed to receive a line: timeout \\[\\]\n--- error_log eval\nqr/lua tcp socket read timed out, upstream: localhost:\\d+\\(127.0.0.1\\)/\n\n\n\n=== TEST 2: send timeout\n--- config\n    server_tokens off;\n    location /t {\n        resolver $TEST_NGINX_RESOLVER ipv6=off;\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n\n            sock:settimeouts(500, 500, 500)  -- 500ms timeout\n\n            local port = ngx.var.server_port\n            local ok, err = sock:connect(\"localhost\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            local data = \"slowdata\" -- slow data\n            local num = 10\n\n            local req = \"POST /foo HTTP/1.0\\r\\nHost: localhost\\r\\nContent-Length: \"\n                        .. #data * num .. \"\\r\\nConnection: close\\r\\n\\r\\n\"\n\n            local bytes, err = sock:send(req)\n            if not bytes then\n                ngx.say(\"failed to send request: \", err)\n                return\n            end\n\n            for i = 1, num do\n                local bytes, err = sock:send(data)\n                if not bytes then\n                    ngx.say(\"failed to send body: \", err)\n                    return\n                end\n            end\n\n            while true do\n                local line, err, part = sock:receive()\n                if line then\n                    ngx.say(\"received: \", line)\n\n                else\n                    ngx.say(\"failed to receive a line: \", err, \" [\", part, \"]\")\n                    break\n                end\n            end\n\n            sock:close()\n        }\n    }\n\n    location /foo {\n        content_by_lua_block {\n            local content_length = ngx.req.get_headers()[\"Content-Length\"]\n\n            local sock = ngx.req.socket()\n\n            sock:settimeouts(500, 500, 500)\n\n            local chunk = 8\n\n            for i = 1, content_length, chunk do\n                local data, err = sock:receive(chunk)\n                if not data then\n                    ngx.say(\"failed to receive chunk: \", err)\n                    return\n                end\n            end\n\n            ngx.say(\"got len: \", content_length)\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nfailed to send body: timeout\n--- error_log eval\nqr/lua tcp socket write timed out, upstream: localhost:\\d+\\(127.0.0.1\\)/\n\n\n\n=== TEST 3: read timeout\n--- config\n    server_tokens off;\n    location /t {\n        resolver $TEST_NGINX_RESOLVER ipv6=off;\n        content_by_lua_block {\n            local function do_req(uri)\n                local sock = ngx.socket.tcp()\n\n                sock:settimeouts(150, 150, 150)  -- 150ms\n\n                local port = ngx.var.server_port\n                local ok, err = sock:connect(\"localhost\", port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                local req = \"GET \" .. uri .. \" HTTP/1.1\\r\\nHost: localhost\\r\\n\\r\\n\"\n\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send request: \", err)\n                    return\n                end\n\n                local reader = sock:receiveuntil(\"\\r\\n0\\r\\n\\r\\n\")\n                local data, err = reader()\n\n                if not data then\n                    ngx.say(\"failed to receive response body: \", err)\n                    return\n                end\n\n                ngx.say(\"received response of \", #data, \" bytes\")\n\n                local ok, err = sock:setkeepalive()\n                if not ok then\n                    ngx.say(\"failed to set reusable: \", err)\n\n                    sock:setkeepalive()\n                end\n            end\n\n            do_req(\"/foo\")\n            do_req(\"/bar\")\n        }\n    }\n\n    location /foo {\n        content_by_lua_block {\n            ngx.say(\"foo\")\n            ngx.flush()\n            ngx.say(\"end\")\n        }\n        more_clear_headers Date;\n    }\n\n    location /bar {\n        content_by_lua_block {\n            ngx.sleep(0.2)\n            ngx.say(\"bar\")\n            ngx.flush()\n            ngx.say(\"end\")\n        }\n        more_clear_headers Date;\n    }\n\n--- request\nGET /t\n--- response_body_like\nreceived response of 128 bytes\nfailed to receive response body: timeout\n--- error_log eval\nqr/lua tcp socket read timed out, upstream: localhost:\\d+\\(127.0.0.1\\)/\n\n\n\n=== TEST 4: send timeout keepalive\n--- config\n    server_tokens off;\n    location /t {\n        resolver $TEST_NGINX_RESOLVER ipv6=off;\n        content_by_lua_block {\n            local function do_req(uri, data)\n                local sock = ngx.socket.tcp()\n\n                sock:settimeouts(500, 500, 500)  -- 500ms timeout\n\n                local port = ngx.var.server_port\n                local ok, err = sock:connect(\"localhost\", port)\n                if not ok then\n                    ngx.say(\"failed to connect: \", err)\n                    return\n                end\n\n                local num = 10\n\n                local req = \"POST \" .. uri ..\" HTTP/1.1\\r\\nHost: localhost\\r\\nContent-Length: \"\n                            .. #data * num .. \"\\r\\nConnection: close\\r\\n\\r\\n\"\n\n                local bytes, err = sock:send(req)\n                if not bytes then\n                    ngx.say(\"failed to send request: \", err)\n                    return\n                end\n\n                for i = 1, num do\n                    local bytes, err = sock:send(data)\n                    if not bytes then\n                        ngx.say(\"failed to send body: \", err)\n                        return\n                    end\n                end\n\n                local reader = sock:receiveuntil(\"\\r\\n0\\r\\n\\r\\n\")\n                local data, err = reader()\n\n                if not data then\n                    ngx.say(\"failed to receive response body: \", err)\n                    return\n                end\n\n                ngx.say(\"received response of \", #data, \" bytes\")\n\n                local ok, err = sock:setkeepalive()\n                if not ok then\n                    ngx.say(\"failed to set reusable: \", err)\n\n                    sock:setkeepalive()\n                end\n            end\n\n            do_req(\"/foo\", \"quicdata\")\n            do_req(\"/foo\", \"slowdata\")\n        }\n    }\n\n    location /foo {\n        content_by_lua_block {\n            ngx.say(\"hello world\")\n        }\n    }\n\n--- request\nGET /t\n--- response_body\nreceived response of 159 bytes\nfailed to send body: timeout\n--- error_log eval\nqr/lua tcp socket write timed out, upstream: localhost:\\d+\\(127.0.0.1\\)/\n"
  },
  {
    "path": "t/303-udp-socket-error-log.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket::Lua;\n\nrepeat_each(2);\n\nif (defined $ENV{TEST_NGINX_SKIP_COSOCKET_LOG_TEST}) {\n    plan(skip_all => \"Remove TEST_NGINX_SKIP_COSOCKET_LOG_TEST to enable this test\");\n} else {\n    plan tests => repeat_each() * (3 * blocks());\n}\n\nour $HtmlDir = html_dir;\n\n$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;\n$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';\n\nlog_level 'debug';\n\nno_long_string();\n#no_diff();\n#no_shuffle();\ncheck_accum_error_log();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: access a TCP interface\ntest-nginx use the same port for tcp(http) and udp(http3)\nso need to change to a port that is not listen by any app.\ndefault port range:\nnet.ipv4.ip_local_port_range = 32768\t60999\nchoose a port greater than 61000 should be less race.\n--- config\n    server_tokens off;\n    location /t {\n        resolver $TEST_NGINX_RESOLVER ipv6=off;\n        set $port 65432;\n\n        content_by_lua '\n            local socket = ngx.socket\n            -- local socket = require \"socket\"\n\n            local udp = socket.udp()\n\n            local port = ngx.var.port\n            udp:settimeout(1000) -- 1 sec\n\n            local ok, err = udp:setpeername(\"localhost\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected\")\n\n            local req = \"\\\\0\\\\1\\\\0\\\\0\\\\0\\\\1\\\\0\\\\0flush_all\\\\r\\\\n\"\n            local ok, err = udp:send(req)\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local data, err = udp:receive()\n            if not data then\n                ngx.say(\"failed to receive data: \", err)\n                return\n            end\n            ngx.print(\"received \", #data, \" bytes: \", data)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected\nfailed to receive data: connection refused\n--- error_log eval\nqr/recv\\(\\) failed \\(\\d+: Connection refused\\), upstream: localhost:65432\\(127.0.0.1\\)/\n\n\n\n=== TEST 2: recv timeout\n--- config\n    server_tokens off;\n    location /t {\n        resolver $TEST_NGINX_RESOLVER ipv6=off;\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.udp()\n            sock:settimeout(100) -- 100 ms\n\n            local ok, err = sock:setpeername(\"localhost\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n\n            -- ok, err = sock:close()\n            -- ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: timeout\n--- error_log\nlua udp socket read timed out, upstream: localhost:11211(127.0.0.1)\n\n\n\n=== TEST 3: read timeout and re-receive\n--- config\n    location = /t {\n        resolver $TEST_NGINX_RESOLVER ipv6=off;\n        content_by_lua '\n            local udp = ngx.socket.udp()\n            udp:settimeout(80)\n            local ok, err = udp:setpeername(\"localhost\", 19232)\n            if not ok then\n                ngx.say(\"failed to setpeername: \", err)\n                return\n            end\n            local ok, err = udp:send(\"blah\")\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n            for i = 1, 2 do\n                local data, err = udp:receive()\n                if err == \"timeout\" then\n                    -- continue\n                else\n                    if not data then\n                        ngx.say(\"failed to receive: \", err)\n                        return\n                    end\n                    ngx.say(\"received: \", data)\n                    return\n                end\n            end\n\n            ngx.say(\"timed out\")\n        ';\n    }\n--- udp_listen: 19232\n--- udp_reply: hello world\n--- udp_reply_delay: 100ms\n--- request\nGET /t\n--- response_body\nreceived: hello world\n--- error_log\nlua udp socket read timed out, upstream: localhost:19232(127.0.0.1)\n\n\n\n=== TEST 4: access a TCP interface\n--- config\n    server_tokens off;\n    location /t {\n        resolver $TEST_NGINX_RESOLVER ipv6=off;\n        set $port 65432;\n\n        content_by_lua '\n            local socket = ngx.socket\n            -- local socket = require \"socket\"\n\n            local udp = socket.udp()\n\n            local port = ngx.var.port\n            udp:settimeout(1000) -- 1 sec\n\n            local ok, err = udp:setpeername(\"localhost\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected\")\n\n            local req = \"\\\\0\\\\1\\\\0\\\\0\\\\0\\\\1\\\\0\\\\0flush_all\\\\r\\\\n\"\n            local ok, err = udp:send(req)\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n\n            local data, err = udp:receive()\n            if not data then\n                ngx.say(\"failed to receive data: \", err)\n                return\n            end\n            ngx.print(\"received \", #data, \" bytes: \", data)\n        ';\n    }\n--- request\nGET /t\n--- response_body\nconnected\nfailed to receive data: connection refused\n--- error_log eval\nqr/recv\\(\\) failed \\(\\d+: Connection refused\\), upstream: localhost:65432\\(127.0.0.1\\)/\n\n\n\n=== TEST 5: recv timeout\n--- config\n    server_tokens off;\n    location /t {\n        resolver $TEST_NGINX_RESOLVER ipv6=off;\n        #set $port 5000;\n        set $port $TEST_NGINX_MEMCACHED_PORT;\n\n        content_by_lua '\n            local port = ngx.var.port\n\n            local sock = ngx.socket.udp()\n            sock:settimeout(100) -- 100 ms\n\n            local ok, err = sock:setpeername(\"localhost\", port)\n            if not ok then\n                ngx.say(\"failed to connect: \", err)\n                return\n            end\n\n            ngx.say(\"connected: \", ok)\n\n            local line, err = sock:receive()\n            if line then\n                ngx.say(\"received: \", line)\n\n            else\n                ngx.say(\"failed to receive: \", err)\n            end\n\n            -- ok, err = sock:close()\n            -- ngx.say(\"close: \", ok, \" \", err)\n        ';\n    }\n\n    location /foo {\n        echo foo;\n        more_clear_headers Date;\n    }\n--- request\nGET /t\n--- response_body\nconnected: 1\nfailed to receive: timeout\n--- error_log\nlua udp socket read timed out, upstream: localhost:11211(127.0.0.1)\n\n\n\n=== TEST 6: read timeout and re-receive\n--- config\n    location = /t {\n        resolver $TEST_NGINX_RESOLVER ipv6=off;\n        content_by_lua '\n            local udp = ngx.socket.udp()\n            udp:settimeout(80)\n            local ok, err = udp:setpeername(\"localhost\", 19232)\n            if not ok then\n                ngx.say(\"failed to setpeername: \", err)\n                return\n            end\n            local ok, err = udp:send(\"blah\")\n            if not ok then\n                ngx.say(\"failed to send: \", err)\n                return\n            end\n            for i = 1, 2 do\n                local data, err = udp:receive()\n                if err == \"timeout\" then\n                    -- continue\n                else\n                    if not data then\n                        ngx.say(\"failed to receive: \", err)\n                        return\n                    end\n                    ngx.say(\"received: \", data)\n                    return\n                end\n            end\n\n            ngx.say(\"timed out\")\n        ';\n    }\n--- udp_listen: 19232\n--- udp_reply: hello world\n--- udp_reply_delay: 100ms\n--- request\nGET /t\n--- response_body\nreceived: hello world\n--- error_log\nlua udp socket read timed out, upstream: localhost:19232(127.0.0.1)\n"
  },
  {
    "path": "t/StapThread.pm",
    "content": "package t::StapThread;\n\nuse strict;\nuse warnings;\n\nour $GCScript = <<'_EOC_';\nglobal ids, cur\nglobal in_req = 0\nglobal alive_reqs\n\nfunction gen_id(k) {\n    if (ids[k]) return ids[k]\n    ids[k] = ++cur\n    return cur\n}\n\nF(ngx_http_handler) {\n    if (!alive_reqs[$r] && $r == $r->main) {\n        in_req++\n        alive_reqs[$r] = 1\n\n        if (in_req == 1) {\n            delete ids\n            cur = 0\n        }\n    }\n}\n\nF(ngx_http_free_request) {\n    if (alive_reqs[$r]) {\n        in_req--\n        delete alive_reqs[$r]\n    }\n}\n\nF(ngx_http_terminate_request) {\n    if (alive_reqs[$r]) {\n        in_req--\n        delete alive_reqs[$r]\n    }\n}\n\nM(http-lua-user-thread-spawn) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"spawn user thread %x in %x\\n\", c, p)\n}\n\nM(http-lua-thread-delete) {\n    t = gen_id($arg2)\n    printf(\"delete thread %x\\n\", t)\n}\n\nM(http-lua-user-coroutine-create) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"create %x in %x\\n\", c, p)\n}\n\nM(http-lua-coroutine-done) {\n    t = gen_id($arg2)\n    printf(\"terminate %d: %s\\n\", t, $arg3 ? \"ok\" : \"fail\")\n    #print_ubacktrace()\n}\n\n_EOC_\n\nour $StapScript = <<'_EOC_';\nglobal ids, cur\nglobal timers\nglobal in_req = 0\nglobal co_status\nglobal alive_reqs\n\nfunction gen_id(k) {\n    if (ids[k]) return ids[k]\n    ids[k] = ++cur\n    return cur\n}\n\nF(ngx_http_handler) {\n    if (!alive_reqs[$r] && $r == $r->main) {\n        in_req++\n        alive_reqs[$r] = 1\n\n        printf(\"in req: %d\\n\", in_req)\n\n        if (in_req == 1) {\n            delete ids\n            cur = 0\n            co_status[0] = \"running\"\n            co_status[1] = \"suspended\"\n            co_status[2] = \"normal\"\n            co_status[3] = \"dead\"\n        }\n    }\n}\n\nF(ngx_http_free_request) {\n    if (alive_reqs[$r]) {\n        in_req--\n        println(\"free request\")\n        delete alive_reqs[$r]\n    }\n}\n\nF(ngx_http_terminate_request) {\n    if (alive_reqs[$r]) {\n        in_req--\n        println(\"terminate request\")\n        delete alive_reqs[$r]\n    }\n}\n\nF(ngx_http_lua_post_thread) {\n    id = gen_id($coctx->co)\n    printf(\"post thread %d\\n\", id)\n}\n\nM(timer-add) {\n    timers[$arg1] = $arg2\n    printf(\"add timer %d\\n\", $arg2)\n}\n\nM(timer-del) {\n    printf(\"delete timer %d\\n\", timers[$arg1])\n    delete timers[$arg1]\n}\n\nM(timer-expire) {\n    printf(\"expire timer %d\\n\", timers[$arg1])\n    delete timers[$arg1]\n}\n\nF(ngx_http_lua_sleep_handler) {\n    printf(\"sleep handler called\\n\")\n}\n\nF(ngx_http_lua_run_thread) {\n    id = gen_id($ctx->cur_co_ctx->co)\n    printf(\"run thread %d\\n\", id)\n    #if (id == 1) {\n        #print_ubacktrace()\n    #}\n}\n\nprobe process(\"/usr/local/openresty-debug/luajit/lib/libluajit-5.1.so.2\").function(\"lua_resume\") {\n    id = gen_id($L)\n    printf(\"lua resume %d\\n\", id)\n}\n\nM(http-lua-user-thread-spawn) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"spawn uthread %x in %x\\n\", c, p)\n}\n\nM(http-lua-thread-delete) {\n    t = gen_id($arg2)\n    uthreads = @cast($arg3, \"ngx_http_lua_ctx_t\")->uthreads\n    printf(\"delete thread %x (uthreads %d)\\n\", t, uthreads)\n    #print_ubacktrace()\n}\n\nM(http-lua-run-posted-thread) {\n    t = gen_id($arg2)\n    printf(\"run posted thread %d (status %s)\\n\", t, co_status[$arg3])\n}\n\nM(http-lua-user-coroutine-resume) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"resume %x in %x\\n\", c, p)\n}\n\nM(http-lua-thread-yield) {\n    t = gen_id($arg2)\n    printf(\"thread %d yield\\n\", t)\n}\n\n/*\nF(ngx_http_lua_coroutine_yield) {\n    printf(\"yield %x\\n\", gen_id($L))\n}\n*/\n\nM(http-lua-user-coroutine-yield) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"yield %x in %x\\n\", c, p)\n}\n\nF(ngx_http_lua_atpanic) {\n    printf(\"lua atpanic(%d):\", gen_id($L))\n    print_ubacktrace();\n}\n\nF(ngx_http_lua_run_posted_threads) {\n    printf(\"run posted threads\\n\")\n}\n\nF(ngx_http_finalize_request) {\n    printf(\"finalize request %s: rc:%d c:%d a:%d\\n\", ngx_http_req_uri($r), $rc, $r->main->count, $r == $r->main);\n    #if ($rc == -1) {\n        #print_ubacktrace()\n    #}\n}\nF(ngx_http_lua_post_subrequest) {\n    printf(\"post subreq: %s rc=%d, status=%d a=%d\\n\", ngx_http_req_uri($r), $rc,\n         $r->headers_out->status, $r == $r->main)\n    #print_ubacktrace()\n}\nM(http-subrequest-done) {\n    printf(\"subrequest %s done\\n\", ngx_http_req_uri($r))\n}\nM(http-subrequest-wake-parent) {\n    printf(\"subrequest wake parent %s\\n\", ngx_http_req_uri($r->parent))\n}\nM(http-lua-user-coroutine-create) {\n    p = gen_id($arg2)\n    c = gen_id($arg3)\n    printf(\"create %x in %x\\n\", c, p)\n}\n\nF(ngx_http_lua_ngx_exec) { println(\"exec\") }\n\nF(ngx_http_lua_ngx_exit) { println(\"exit\") }\nF(ngx_http_lua_ffi_exit) { println(\"exit\") }\n\nF(ngx_http_lua_req_body_cleanup) {\n    println(\"lua req body cleanup\")\n}\n\nF(ngx_http_read_client_request_body) {\n    println(\"read client request body\")\n}\n\nF(ngx_http_lua_finalize_coroutines) {\n    println(\"finalize coroutines\")\n}\n\nF(ngx_http_lua_ngx_exit) {\n    println(\"ngx.exit() called\")\n}\n\nF(ngx_http_lua_ffi_exit) {\n    println(\"ngx.exit() called\")\n}\n\nF(ngx_http_lua_sleep_resume) {\n    println(\"lua sleep resume\")\n}\n\nM(http-lua-coroutine-done) {\n    t = gen_id($arg2)\n    printf(\"terminate coro %d: %s, waited by parent:%d, child cocotx: %p\\n\", t, $arg3 ? \"ok\" : \"fail\", $ctx->cur_co_ctx->waited_by_parent, $ctx->cur_co_ctx)\n    //print_ubacktrace()\n}\n\nF(ngx_http_lua_ngx_echo) {\n    println(\"ngx.print or ngx.say\")\n}\n\nF(ngx_http_lua_del_all_threads) {\n    println(\"del all threads\")\n}\n\n/*\nM(http-lua-info) {\n    msg = user_string($arg1)\n    printf(\"lua info: %s\\n\", msg)\n}\n*/\n\nM(http-lua-user-thread-wait) {\n    p = gen_id($arg1)\n    c = gen_id($arg2)\n    printf(\"lua thread %d waiting on %d, child coctx: %p\\n\", p, c, $sub_coctx)\n}\n_EOC_\n\n1;\n"
  },
  {
    "path": "t/cert/dst-ca.crt",
    "content": "# Comodo AAA Services root\n-----BEGIN CERTIFICATE-----\nMIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb\nMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow\nGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj\nYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL\nMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\nBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM\nGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP\nADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua\nBtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe\n3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4\nYgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR\nrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm\nez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU\noBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF\nMAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v\nQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t\nb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF\nAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q\nGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz\nRt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2\nG9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi\nl2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3\nsmPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "t/cert/equifax.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV\r\nUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy\r\ndGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1\r\nMVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx\r\ndWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B\r\nAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f\r\nBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A\r\ncJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC\r\nAwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ\r\nMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm\r\naWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw\r\nODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj\r\nIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF\r\nMAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA\r\nA4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y\r\n7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh\r\n1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "t/cert/gen-test-passphrase.sh",
    "content": "#!/bin/bash\n\n# Set variables\nSUBJECT=\"/C=CN/ST=Guangdong/L=ShenZhen/O=OpenResty/OU=OpenResty/CN=test.com/emailAddress=guanglinlv@gmail.com\"\nDAYS=49000  # Approximately 134 years\nKEY_FILE=\"test_passphrase.key\"\nCERT_FILE=\"test_passphrase.crt\"\nPASSWORD=\"123456\"\n\n# Generate a new 2048-bit RSA private key, encrypted with the password\nopenssl genrsa -aes256 -passout pass:$PASSWORD -out $KEY_FILE 2048\n\n# Generate a new self-signed certificate\nopenssl req -x509 -new -nodes -key $KEY_FILE -sha256 -days $DAYS \\\n    -out $CERT_FILE -subj \"$SUBJECT\" -passin pass:$PASSWORD\n\n# Display information about the new certificate\nopenssl x509 -in $CERT_FILE -text -noout\n\necho \"New 2048-bit certificate generated successfully!\"\necho \"Private key (encrypted): $KEY_FILE\"\necho \"Certificate: $CERT_FILE\"\n\n"
  },
  {
    "path": "t/cert/gen-test-rsa1024.sh",
    "content": "openssl req -new -newkey rsa:1024 -days 3650 -nodes -x509 \\\n    -subj \"/C=US/ST=California/L=San Francisco/O=OpenResty/CN=test2.com/emailAddress=openresty@gmail.com\" \\\n    -keyout test-rsa1024.key -out test-rsa1024.crt\n"
  },
  {
    "path": "t/cert/gen-test2.sh",
    "content": "openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \\\n    -subj \"/C=US/ST=California/L=San Francisco/O=OpenResty/CN=test2.com/emailAddress=openresty@gmail.com\" \\\n    -keyout test2.key -out test2.crt\n"
  },
  {
    "path": "t/cert/http3/http3.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFCTCCAvGgAwIBAgIUHLeNm7JwH368JWXBYJ1Dv+xcL6kwDQYJKoZIhvcNAQEL\nBQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIxMTEwNTA2NTQxNVoXDTQxMDEw\nNDA2NTQxNVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF\nAAOCAg8AMIICCgKCAgEAx5/M18tKWKfacgldf9gBguTdLA3JWiblRWM/38fSZGKL\nMcb1vLErY/qQyLYxoLKBht0FUZAEZ08y/iheYFZT7H082b8bNvwY+v6bScOQOvcX\nhkTWNlSQORDH4qIFxVXq0soXga+0ukSZ2RQRcCUWeKUaZwhGCYNIj04/FB9Lef95\nKu7LkNauTBmRGIwXgQWhfnoPM21o9D9i54R9L2RHU7fGeTGhiS0nCe1nPPB7KBgO\ns9rBHMXV9qHxCNMWWiVNsMX049S7aD9yvRrV7NAHssVQdaRR234IPJxb7BYSUPi8\n5U+8l43Ornd+cY/R/sXDQluFidlnZvHT+akdy0ObWDK0lMUweDWvFVLY3ZvMvsGR\nrQE8RR5/fy83y7w//0734EBk4ttEzlilmjA8UvnumOpK0UYaW8LkikvxB+48e89s\nNOEKZf3Mfw/fDRz0tO+cr4cIgUPQ4ru6KNVnGH/ZiD97AVMPXJO9nPOUIRVH9aXM\nwC74CSt5idWOwpTKy+sLg7anM235NvZ9bTiS+V5CTzBlqL/wKzEI0Injds3kBc1a\nY8Lk/cIdNhuwlN01fYluA8XyB3DWoeQYbySEoHC5ksvaFLBM3yPomWrmyM5lJrj5\nQbWa46b0bONJQ2qmrQa5KREVvDthHQvKELsabHX1qbCShSxoG45aclqpmKy2AjcC\nAwEAAaNTMFEwHQYDVR0OBBYEFA2T9Sgv31hCl3INL5MB++NrMu0iMB8GA1UdIwQY\nMBaAFA2T9Sgv31hCl3INL5MB++NrMu0iMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI\nhvcNAQELBQADggIBAJdxtUq4T/sh5Ww7pMOB4+JF5zsxEBpZiFMzT2hYZ+6a2mYh\nUTy7ff1lNfNl0BfslaOD404Qt0SSgI2TByLs838/sKpVk+QePcw6kl6IFIPERUBO\nuxq/+QXqsDHOXb6m4qpEBmrknUa65dThdUw5r4sHo0XD2l/y2/zEZtd4e3ZGAWts\nspdjPBk4UHPtG3p29eY/8ehw+zDXuWG4nlJYjn+6PRnLhvpgt2/E/Wr9PeYcv3IC\nUgavxXVtk9fnclg8BuKlnZYki7txn8+F9Rh03CVVZ9R16Q/aVALI2iTs8T4gCt7Z\neexfLGfBkLBkLvpL7wpzxNsvOC2b5bJZCDUTprLVmdpmMQu75qLg1mOfubMo983H\n8G91V4XOokRoCRub/SLKA16/gpEwnE2aDsVMUVSxwpRu2Rjw4GpzbCNAHUzmblrh\nzYMSAsEuTcsZEAdZQrzmhGc1Yg5Q88V4o+qyywzkgd86O65QUozhnkCs+eS9ikMV\ncPLXoW5SDIsrrcoTR6bH5MdDjS7ILKUUC5+x0qo6EhK94Fx49TkRBNIYk3o0fG7j\no/0YvozXjqTRnodYegL4LKoGZyfL4qbuh3t8ZGQ/Z0ECmvjcmJzPyObIiMe2InT3\nGRY+ypPTyeiumjHFFVO0zx+DAv+HFPtq1XaygWvxKY1DTP6FNN0BzQdzAgKm\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "t/cert/http3/http3.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDHn8zXy0pYp9py\nCV1/2AGC5N0sDclaJuVFYz/fx9JkYosxxvW8sStj+pDItjGgsoGG3QVRkARnTzL+\nKF5gVlPsfTzZvxs2/Bj6/ptJw5A69xeGRNY2VJA5EMfiogXFVerSyheBr7S6RJnZ\nFBFwJRZ4pRpnCEYJg0iPTj8UH0t5/3kq7suQ1q5MGZEYjBeBBaF+eg8zbWj0P2Ln\nhH0vZEdTt8Z5MaGJLScJ7Wc88HsoGA6z2sEcxdX2ofEI0xZaJU2wxfTj1LtoP3K9\nGtXs0AeyxVB1pFHbfgg8nFvsFhJQ+LzlT7yXjc6ud35xj9H+xcNCW4WJ2Wdm8dP5\nqR3LQ5tYMrSUxTB4Na8VUtjdm8y+wZGtATxFHn9/LzfLvD//TvfgQGTi20TOWKWa\nMDxS+e6Y6krRRhpbwuSKS/EH7jx7z2w04Qpl/cx/D98NHPS075yvhwiBQ9Diu7oo\n1WcYf9mIP3sBUw9ck72c85QhFUf1pczALvgJK3mJ1Y7ClMrL6wuDtqczbfk29n1t\nOJL5XkJPMGWov/ArMQjQieN2zeQFzVpjwuT9wh02G7CU3TV9iW4DxfIHcNah5Bhv\nJISgcLmSy9oUsEzfI+iZaubIzmUmuPlBtZrjpvRs40lDaqatBrkpERW8O2EdC8oQ\nuxpsdfWpsJKFLGgbjlpyWqmYrLYCNwIDAQABAoICAENb1qESRbn4matVIamb15a1\nZzQQStsSuNZbERiPspyQ6+sV+aF8HuoTiHtRjxlsYmyBc+P7tqCthsVgFchoGNV5\nxOispaA+HKfE9d1EEgzzh4qU+7tFeYzn7qq4hT37KcuKybfG9DLOJyOqs9+lhBmd\njHUrw4Y+OGOywXImxS8bV2V3QlVTO2kOT3l6/AtbPQ0SXsK5rmqMYPFCMYOmULMd\nFembJ6jEBaJB604Sz1vOElf5/qOY1gPszQpvP+GXKMn3YhTmmX4puqu4vGq2H4Lh\nNa8cjUqFEn5xPEtDf1a3N/Ygm8B/5zfTtmTXZMKVNLfVbg//vfZsr1xVBmqqG2Zk\nPUxZ9pyRn25zAYIgIC6DBgmM32S5Zl0axeoWIHdgOTAOwqLLF/ZFozdVSN+skJhl\nq1ndTw+MmVHi19HDUt8YfIICn8g+Dw9NRdX+VjXrRMBweCL41JzHzP6YKo73VJty\nWIVt/LwH2sxdGH0QeP6+Dajel3/ouXAcS31ZpeWZ0QUfa5I0DOn50mvZSuaC0m7Z\nbaHBGqawq9EKdjbU5WN6popOCWCx23BZDorKmPGBQ4x54LxfUeet0HAYjsvzUZ0q\na/AC6pMMHUjG4i5dPhOAhjxkpwfhLjIBTC8VrelGfap50Mlst5InCjClUsSKg43w\ntsRg0HQflxYsPXUGtBqxAoIBAQD29Q6gPNsd7JMzmVzK7fy5YaKq7aI/vOU/tp7U\nLQqhBfsy70U3UNsclsQQ/1RG2cgpFVctlpE8ITpFqSlUDRtlvrK9NaIPEYBV84Xn\nbWPqFjGiRowuXiCR8axbPnsEquO2+VHNuVD/PsCocvbpqtpbfEGmcuyWiO4uFMyW\nbeOcRxEsn3U7ABZGDIhxYjpmkiVMu1pRjj+ddqtNMia398K86c3ETRg+zrHnpzio\nkuhxv6TjtnXT1ibI0yKzdQ5sKpa3DYUN/wJgeFVHsSuCZjCzufcMdjCjG3kTTljT\nFrMbweISSTJNs6KsbCqzmc6dmpwuU1ySX07F9sgdJ/7lBCXTAoIBAQDO7wy9Zr8C\nRidte1EaMP04OSfqc2Jha/U2te47R1VDyHKzpFHvHwWHqFng6J181M9iubUsh/63\nd12uyjZoToMgT7AJ7PMwbEKJbMxpIjqF7Dp1aM11TjRq0mQbZ3wkeuYdK7jYc92j\nX4hF+tJpCYo/0lMA0UFYhMyUPbi33xqlJKLghVncBYmKHluf3k0g73eRg4MGlYHG\ntZ5qVdgwFTztpBb4ySJMOYmjb/OoYPdfoLC8P4ZoHiax4MU30R8b/oGCmYzwOsMC\ndNz9dZgeCFo3+lrzMUnIh3y+YcGo2JJ9ZW1LxGvEIU0aEQ7mSPV8k7m3QiH2MiPQ\nP0t17w1hQP+NAoIBAHJch1pi9CGGZaB2e8cpsGf0s8yt4P3dLthzbFfbR9nLmEk9\nDnOQSPeTRdaNNuzce1mzHTzqRfVvebm6nX3j1/Uk+0atqI+Lzj9/V1oViThk8LUy\nMEZkpnaPUP6sD3HY5TzddilrkPuyhqs7GeaZjSbigtBe1frcDFhgn2FmIApFysk8\nSqB46NelhCXllB/du9ItzKSJ2CHGS4ujFtUIsjCjoPsvrHOhajdZc950sZnDYstk\numnP+QP06lPqeDRVAJhidWRG3EXqU6uwevKW+iSwkJw/u0Q9O7NaC74s++J1xYgs\nR1Q+RK3OJXQoXMsVRxAY4HyUEDmSj5cY52wMoKsCggEAQMXu9PJOY8XV3Z02G76t\n5IVviyGm79u9G+0Cryd69watcLHEu9a4AmieCZqGgWaTq9F5doDzKDaC6o19TlUV\nEm4fKlwzGzsn8KBPs7D1JKp2+f1eIpPiMHW+xB02bKzTjtn6uDY8cEEdBNqoNhy4\nW5XYSW82xyB6cQSI53U8f+jh2umi4Q4SqVsrTvVkqySKBtBlmQ//WVXMSniofRSI\nx9IPJry+saFpBfGrEU+Y3yQLbkFsLvcRIai70ubwl/CoVVr/FMsv83rlGalPfkcb\nBl6lTW5mLBDM6ULsPY/c+sde2NKY8QGDgt9IDKlVvjL3dPeMbeXv8+V8F2RGieSw\nmQKCAQEA1n4t4CLKZMWZ4oonBEp8u9rjK23ObCSxANsfoIw94v2zHTFqLraFg9aH\ns+DIc6M/XWX+Ie55v1QuYt8LrMtc9/rtOJdISybAtnheSqjQ+IkTEvBab/8pqhYg\nJhv/RxmBnCLwiIzRGVpxv9/5bbKXq1JKgdQKO3RBG53lcFFMO1O5srhKqj2KgAHv\nXCQxBmtj83e6gp6hvUOU4YC2aKyL9QqNVndGzPJikEguyvJPUFw6RoB0StVQnzLY\nUOgIH+8VTjzL90nQ/JitVT8uGdw1Ge5xJfCXDe/PEYpDsY65DKPrIMWFeQs6T/Mb\nnvNeBReQOuYYpcCc2GM96RMUz3iyoA==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "t/cert/mtls_ca.crt",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number:\n            32:ed:21:56:d8:4e:aa:03:89:a9:4a:a4:e2:85:2d:8a:3b:2b:89:22\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C = US, ST = California, O = OpenResty, CN = OpenResty Testing Root CA\n        Validity\n            Not Before: Mar 13 15:49:00 2022 GMT\n            Not After : Mar  8 15:49:00 2042 GMT\n        Subject: C = US, ST = California, O = OpenResty, CN = OpenResty Testing Root CA\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:e6:37:d2:c6:17:36:c7:b2:7f:7d:cf:d0:62:87:\n                    99:d9:21:b8:de:ff:d8:e2:3a:1c:68:90:8f:ce:17:\n                    68:22:b0:60:30:cc:29:e8:34:ee:ff:b2:25:de:6e:\n                    1a:d4:df:10:19:11:4b:40:61:d3:a9:4d:80:ed:97:\n                    81:4e:c5:74:e8:4d:63:e3:5f:21:bc:5a:6e:22:a0:\n                    17:91:c1:cb:25:53:9b:9d:4e:e1:51:5b:f6:52:e7:\n                    0a:27:f6:16:c2:31:cb:6c:47:f4:89:51:15:cc:06:\n                    be:31:3e:1c:ea:ee:81:9b:c4:97:96:fd:e5:1c:95:\n                    9e:c0:65:cd:a9:9a:cb:68:67:f2:62:a0:21:eb:5a:\n                    c5:a1:92:ed:32:41:28:f9:47:34:eb:44:ae:d6:e7:\n                    76:71:11:98:c9:2e:ce:6c:7c:10:1b:c7:4c:c3:14:\n                    89:4e:d9:4c:d9:c7:43:e9:3c:29:ca:62:a9:91:b3:\n                    87:e7:d7:b4:18:ab:65:f9:6b:ed:82:ca:a1:36:35:\n                    18:05:cb:5c:24:26:13:13:f8:99:ac:99:be:9b:a6:\n                    73:df:0d:16:95:b1:dc:be:fe:7a:c2:b6:dc:c8:93:\n                    cf:10:e0:29:03:0e:28:78:18:84:ee:14:92:ab:be:\n                    5a:a0:14:a2:4a:2f:d3:d0:b8:0e:00:d2:5a:cd:e4:\n                    bd:a1\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Key Usage: critical\n                Certificate Sign, CRL Sign\n            X509v3 Basic Constraints: critical\n                CA:TRUE\n            X509v3 Subject Key Identifier: \n                F0:D7:4B:14:73:E1:67:00:6B:54:B4:19:20:76:12:9F:9D:8E:C8:09\n    Signature Algorithm: sha256WithRSAEncryption\n         6d:52:21:6d:6e:8c:e5:4a:28:07:65:6d:d8:7c:23:2e:c6:c1:\n         d0:ec:27:b3:b0:c3:d3:e8:fa:72:b9:de:32:4e:ff:97:8d:86:\n         a9:6d:b3:a9:b4:2d:77:ca:28:97:6a:3d:7b:a2:15:ed:34:dc:\n         72:9f:6f:e7:01:0c:d3:28:6a:80:1b:50:09:fd:d7:2c:d8:92:\n         d5:10:c4:73:15:20:7d:99:dc:de:30:7b:3c:6e:e9:66:b2:0e:\n         4e:1a:c1:51:57:6e:5b:b0:a9:f6:ff:0b:8f:07:67:31:40:5b:\n         11:a9:06:d3:d3:76:c5:d2:56:95:9a:9e:4a:16:44:4b:32:e5:\n         af:dd:4b:4d:5d:57:b8:85:69:36:93:2a:c6:0c:8f:e1:42:35:\n         be:8e:f3:e7:35:d3:2c:3a:03:31:40:75:8e:e8:dd:57:35:20:\n         5e:18:a9:76:ce:85:be:7e:3a:cf:6e:08:58:5b:47:d5:e9:c4:\n         ec:0e:e9:8e:3c:2d:5c:7b:59:20:5b:24:92:a0:e0:1e:a3:5a:\n         67:d8:ff:7f:a5:82:f1:df:db:05:65:79:88:b1:3c:e6:01:d1:\n         5a:c7:d2:6e:9a:e6:a2:da:4a:c7:19:78:d9:14:71:6e:1f:70:\n         f3:41:e5:b3:78:31:d5:22:0e:7c:1a:b2:43:d9:86:ff:53:ea:\n         2b:ba:d2:27\n-----BEGIN CERTIFICATE-----\nMIIDhDCCAmygAwIBAgIUMu0hVthOqgOJqUqk4oUtijsriSIwDQYJKoZIhvcNAQEL\nBQAwWjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAoT\nCU9wZW5SZXN0eTEiMCAGA1UEAxMZT3BlblJlc3R5IFRlc3RpbmcgUm9vdCBDQTAe\nFw0yMjAzMTMxNTQ5MDBaFw00MjAzMDgxNTQ5MDBaMFoxCzAJBgNVBAYTAlVTMRMw\nEQYDVQQIEwpDYWxpZm9ybmlhMRIwEAYDVQQKEwlPcGVuUmVzdHkxIjAgBgNVBAMT\nGU9wZW5SZXN0eSBUZXN0aW5nIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQDmN9LGFzbHsn99z9Bih5nZIbje/9jiOhxokI/OF2gisGAwzCno\nNO7/siXebhrU3xAZEUtAYdOpTYDtl4FOxXToTWPjXyG8Wm4ioBeRwcslU5udTuFR\nW/ZS5won9hbCMctsR/SJURXMBr4xPhzq7oGbxJeW/eUclZ7AZc2pmstoZ/JioCHr\nWsWhku0yQSj5RzTrRK7W53ZxEZjJLs5sfBAbx0zDFIlO2UzZx0PpPCnKYqmRs4fn\n17QYq2X5a+2CyqE2NRgFy1wkJhMT+Jmsmb6bpnPfDRaVsdy+/nrCttzIk88Q4CkD\nDih4GITuFJKrvlqgFKJKL9PQuA4A0lrN5L2hAgMBAAGjQjBAMA4GA1UdDwEB/wQE\nAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTw10sUc+FnAGtUtBkgdhKf\nnY7ICTANBgkqhkiG9w0BAQsFAAOCAQEAbVIhbW6M5UooB2Vt2HwjLsbB0Owns7DD\n0+j6crneMk7/l42GqW2zqbQtd8ool2o9e6IV7TTccp9v5wEM0yhqgBtQCf3XLNiS\n1RDEcxUgfZnc3jB7PG7pZrIOThrBUVduW7Cp9v8LjwdnMUBbEakG09N2xdJWlZqe\nShZESzLlr91LTV1XuIVpNpMqxgyP4UI1vo7z5zXTLDoDMUB1jujdVzUgXhipds6F\nvn46z24IWFtH1enE7A7pjjwtXHtZIFskkqDgHqNaZ9j/f6WC8d/bBWV5iLE85gHR\nWsfSbprmotpKxxl42RRxbh9w80Hls3gx1SIOfBqyQ9mG/1PqK7rSJw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "t/cert/mtls_ca.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA5jfSxhc2x7J/fc/QYoeZ2SG43v/Y4jocaJCPzhdoIrBgMMwp\n6DTu/7Il3m4a1N8QGRFLQGHTqU2A7ZeBTsV06E1j418hvFpuIqAXkcHLJVObnU7h\nUVv2UucKJ/YWwjHLbEf0iVEVzAa+MT4c6u6Bm8SXlv3lHJWewGXNqZrLaGfyYqAh\n61rFoZLtMkEo+Uc060Su1ud2cRGYyS7ObHwQG8dMwxSJTtlM2cdD6TwpymKpkbOH\n59e0GKtl+WvtgsqhNjUYBctcJCYTE/iZrJm+m6Zz3w0WlbHcvv56wrbcyJPPEOAp\nAw4oeBiE7hSSq75aoBSiSi/T0LgOANJazeS9oQIDAQABAoIBAQDhH9+uNE8uUv/X\nMNvvLfklWpOlBf25o+fZ3NuzRjJgEafOsCee2fyI8FWVwIfeeE8OpFm5GLDZk1+r\ndwdM10xuSheO5Z1gyfF/TJwfvamA09SNrPArFkm3YhUNZNl2hykMtwSLL06oWEOu\ndbXjit4VS9aNIbTlEe7O5/6Ih0W3zmr1yvUua2swmAZMx3GFA4kbjZZ9vDs27sdu\nK+VY3DYRbq1HkiNFT0otfke5bObFBCG7Yp8JLyhYaIkGYFoBXuZ6JNY8EuU2+YyP\n6r40tJ7StR1Q6eZJh9/1leaYGZLCh5oFyKpilTuxHbRbr5A28RJKjKvPsdDgTtQn\nyHGg70FRAoGBAOhC3TQlFcT2WCCZHHql9JEEHnHVBWnL3Jg7VJuL1i6pEIz7qQkW\nAtBEIY/nnTcVNfJ6eXznYtutYvvRSgQTUsBNRoj3s1z9wKOo4uw4LoIUXDEmHCr+\n49DiQyIO21SNMHA+dVxvGRDDjLI9Uc+Scb64QOodoX75HLRZG++24mtdAoGBAP2/\ngCjga2p8Jx9UnhIcrEIIGANyxEQeBdhF56Nt9CJy/Iwi3a6qQ/GkbeoDm5FhXnXo\nxcBaHyv2lwi4uO/hONY8eRnYxAWMwAKMZe6VnU1hWI2Ytkh+OcMPMh7NIGQf6X1o\nJZrBtnTms060TuuDjLeIlaubDR/xDrMWTMKjKbsVAoGAVLuYAZ8J6xpIGlRhbGlA\n6OrMxJCHcgpahvsWKc0BLXKmRBjHmTX7fslsSRihZWgKj1SZH7U2fpgpxV6cFxKJ\nnPhUJEHhoKo+bjZ92tnANdqBq7iQjCsDJ8Bz52fuIlGD+1795+PsDA6bNKdkQkrV\nzlNf80kuEqmFDFJ5+6EHx00CgYAf+jkpbZa71aeMgDpnZ+uhaqm0DYuEVhBAgBa/\n9sRUbw86jc5IC7cCRcmAOzIosQ+ZZls9cV4KSUohVD4iJMzn2rkcM8AIPwOXjp/t\n4DbxoHnrZjpaimW3Gjwju5AAbjEbl7tddFoNA2HHYlurvGlIW9MYzDJsOxGyKfZE\ndRF2PQKBgQDUKNHgDYEjLJ99S5Fm5zN/64bKzzDtktGdqOxik5pBKcs/BvOdLM0i\neCjGz/3qrEoenFIBwF/IRz3ug90Zr8bWOu6DudReflAKI/N13dZ2gOTAfaX4ljJF\nw0ohSi6xs+mu1GmtipGtNxHi/J3na2BeSnSRFSUg6Zd+oh8BZQKmNg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "t/cert/mtls_cert_gen/.gitignore",
    "content": "*.pem\n*.csr\ncfssl\ncfssljson\n"
  },
  {
    "path": "t/cert/mtls_cert_gen/generate.sh",
    "content": "#!/bin/bash\n\nrm *.pem *.csr cfssl cfssljson\n\nwget -O cfssl https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssl_1.6.1_linux_amd64\nwget -O cfssljson https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssljson_1.6.1_linux_amd64\nchmod +x cfssl cfssljson\n\n./cfssl gencert -initca -config profile.json mtls_ca.json | ./cfssljson -bare mtls_ca\n\n./cfssl gencert -ca mtls_ca.pem -ca-key mtls_ca-key.pem -config profile.json -profile=client mtls_client.json | ./cfssljson -bare mtls_client\n./cfssl gencert -ca mtls_ca.pem -ca-key mtls_ca-key.pem -config profile.json -profile=server mtls_server.json | ./cfssljson -bare mtls_server\n\nopenssl x509 -in mtls_ca.pem -text > ../mtls_ca.crt\nmv mtls_ca-key.pem ../mtls_ca.key\n\nopenssl x509 -in mtls_client.pem -text > ../mtls_client.crt\nmv mtls_client-key.pem ../mtls_client.key\n\nopenssl x509 -in mtls_server.pem -text > ../mtls_server.crt\nmv mtls_server-key.pem ../mtls_server.key\n\nrm *.pem *.csr cfssl cfssljson\n"
  },
  {
    "path": "t/cert/mtls_cert_gen/mtls_ca.json",
    "content": "{\n  \"CA\": {\n    \"expiry\": \"175200h\",\n    \"pathlen\": 0\n  },\n  \"CN\": \"OpenResty Testing Root CA\",\n  \"key\": {\n    \"algo\": \"rsa\",\n    \"size\": 2048\n  },\n  \"names\": [\n  {\n    \"C\": \"US\",\n    \"O\": \"OpenResty\",\n    \"ST\": \"California\"\n  }\n ]\n}\n"
  },
  {
    "path": "t/cert/mtls_cert_gen/mtls_client.json",
    "content": "{\n  \"CN\": \"foo@example.com\",\n  \"key\": {\n    \"algo\": \"rsa\",\n    \"size\": 2048\n  },\n  \"names\": [\n  {\n    \"C\": \"US\",\n    \"O\": \"OpenResty\",\n    \"ST\": \"California\"\n  }\n  ],\n  \"hosts\": [\n    \"foo@example.com\",\n    \"bar@example.com\"\n  ]\n}\n"
  },
  {
    "path": "t/cert/mtls_cert_gen/mtls_server.json",
    "content": "{\n  \"CN\": \"example.com\",\n  \"key\": {\n    \"algo\": \"rsa\",\n    \"size\": 2048\n  },\n  \"names\": [\n  {\n    \"C\": \"US\",\n    \"O\": \"OpenResty\",\n    \"ST\": \"California\"\n  }\n  ],\n  \"hosts\": [\n    \"example.com\"\n  ]\n}\n"
  },
  {
    "path": "t/cert/mtls_cert_gen/profile.json",
    "content": "{\n  \"signing\": {\n    \"default\": {\n      \"expiry\": \"175200h\"\n    },\n    \"profiles\": {\n      \"server\": {\n        \"usages\": [\n          \"signing\",\n          \"digital signing\",\n          \"key encipherment\",\n          \"server auth\"\n        ],\n        \"expiry\": \"175199h\"\n      },\n      \"client\": {\n        \"usages\": [\n          \"signing\",\n          \"digital signature\",\n          \"key encipherment\",\n          \"client auth\"\n        ],\n        \"expiry\": \"175199h\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "t/cert/mtls_client.crt",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number:\n            19:0a:a3:a8:9c:d4:0f:dc:c6:fa:23:7b:f8:fc:bd:f4:73:4e:7e:b1\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C = US, ST = California, O = OpenResty, CN = OpenResty Testing Root CA\n        Validity\n            Not Before: Mar 13 15:49:00 2022 GMT\n            Not After : Mar  8 14:49:00 2042 GMT\n        Subject: C = US, ST = California, O = OpenResty, CN = foo@example.com\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:be:5b:09:4c:94:71:d3:82:54:4a:42:6a:76:aa:\n                    34:5d:28:d9:45:e6:44:9a:74:9f:a6:e6:78:49:9e:\n                    c6:20:75:32:5f:92:3b:ec:6e:4b:7b:b0:75:1c:75:\n                    09:00:05:77:d6:59:ca:55:5b:13:b6:76:3a:c6:18:\n                    dc:37:6a:20:93:e6:26:56:5d:0b:96:8c:01:f2:96:\n                    38:08:08:36:a2:64:12:21:a0:8d:48:cd:9a:26:78:\n                    92:29:b6:63:eb:14:d9:b6:e5:87:f7:d5:55:a4:cc:\n                    53:1c:a3:7c:b8:bd:ad:7c:a4:d4:86:1f:a7:1c:43:\n                    c5:1a:b5:f1:03:bd:fe:19:98:1d:b7:13:2b:93:a2:\n                    2a:0e:21:7e:42:a9:bb:28:69:49:59:e7:89:0e:7d:\n                    5a:ce:fb:d4:0c:20:6a:e1:db:b2:6a:e5:a7:55:e0:\n                    d0:58:4a:e2:08:78:82:b9:06:0c:65:f9:24:06:e6:\n                    8a:13:b2:9a:ef:1b:4a:b2:3a:b4:98:7f:dd:3c:0e:\n                    85:0b:a6:c6:47:2f:63:c2:73:52:41:db:7c:06:c3:\n                    2a:b5:2d:d1:e1:30:d5:c4:79:c9:b9:35:68:46:ad:\n                    c4:45:57:ea:11:88:27:37:ed:ac:49:2d:c4:d6:c6:\n                    a6:74:8d:d3:bc:e0:d9:69:25:0c:0c:b0:e3:b7:cb:\n                    8d:99\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Key Usage: critical\n                Digital Signature, Key Encipherment\n            X509v3 Extended Key Usage: \n                TLS Web Client Authentication\n            X509v3 Basic Constraints: critical\n                CA:FALSE\n            X509v3 Subject Key Identifier: \n                22:70:5E:30:8C:4D:66:39:E7:60:C9:29:A2:ED:95:32:34:63:5C:C0\n            X509v3 Authority Key Identifier: \n                keyid:F0:D7:4B:14:73:E1:67:00:6B:54:B4:19:20:76:12:9F:9D:8E:C8:09\n\n            X509v3 Subject Alternative Name: \n                email:foo@example.com, email:bar@example.com\n    Signature Algorithm: sha256WithRSAEncryption\n         96:e7:2a:fc:2a:56:16:80:e2:d3:79:0c:46:db:c3:88:ab:d3:\n         ef:39:66:4b:a9:ab:6c:0e:30:08:07:7c:fc:03:6c:f7:dd:fb:\n         3e:a8:c8:68:28:ab:4e:73:97:80:27:5d:c5:9d:52:00:aa:08:\n         25:c8:f9:dc:df:64:73:a4:58:5b:bd:5f:1a:53:a4:33:a3:b1:\n         45:38:2d:be:d7:f3:a4:c4:f4:7a:07:71:44:f1:a2:65:02:e4:\n         71:84:01:b5:83:4b:de:83:b5:ad:ac:b9:3c:17:42:0c:9a:7d:\n         eb:7f:ab:26:dd:9b:3a:fd:95:37:55:cc:01:c3:3f:20:df:e5:\n         ed:49:51:7a:42:ea:f3:8a:3f:da:6e:c1:1a:11:b9:45:4d:6e:\n         c9:21:f4:e3:4f:31:72:5b:bb:01:92:b6:7f:f1:8a:9e:6c:d0:\n         7f:96:d7:eb:29:09:53:38:26:41:00:f2:33:04:77:bd:a9:ee:\n         60:9e:06:b7:7d:26:ae:1c:4f:56:bd:a5:b6:50:40:be:be:84:\n         2a:54:21:59:47:7d:a5:1e:63:6d:28:36:4d:a6:e4:62:69:9b:\n         9b:fa:2b:48:e8:64:d7:14:f4:62:a2:26:17:a5:05:58:4a:38:\n         d2:44:e7:33:90:b9:c1:8c:85:02:99:b8:03:1a:03:d2:cf:ac:\n         a5:6b:44:98\n-----BEGIN CERTIFICATE-----\nMIID3DCCAsSgAwIBAgIUGQqjqJzUD9zG+iN7+Py99HNOfrEwDQYJKoZIhvcNAQEL\nBQAwWjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAoT\nCU9wZW5SZXN0eTEiMCAGA1UEAxMZT3BlblJlc3R5IFRlc3RpbmcgUm9vdCBDQTAe\nFw0yMjAzMTMxNTQ5MDBaFw00MjAzMDgxNDQ5MDBaMFAxCzAJBgNVBAYTAlVTMRMw\nEQYDVQQIEwpDYWxpZm9ybmlhMRIwEAYDVQQKEwlPcGVuUmVzdHkxGDAWBgNVBAMM\nD2Zvb0BleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nAL5bCUyUcdOCVEpCanaqNF0o2UXmRJp0n6bmeEmexiB1Ml+SO+xuS3uwdRx1CQAF\nd9ZZylVbE7Z2OsYY3DdqIJPmJlZdC5aMAfKWOAgINqJkEiGgjUjNmiZ4kim2Y+sU\n2bblh/fVVaTMUxyjfLi9rXyk1IYfpxxDxRq18QO9/hmYHbcTK5OiKg4hfkKpuyhp\nSVnniQ59Ws771AwgauHbsmrlp1Xg0FhK4gh4grkGDGX5JAbmihOymu8bSrI6tJh/\n3TwOhQumxkcvY8JzUkHbfAbDKrUt0eEw1cR5ybk1aEatxEVX6hGIJzftrEktxNbG\npnSN07zg2WklDAyw47fLjZkCAwEAAaOBozCBoDAOBgNVHQ8BAf8EBAMCBaAwEwYD\nVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUInBeMIxN\nZjnnYMkpou2VMjRjXMAwHwYDVR0jBBgwFoAU8NdLFHPhZwBrVLQZIHYSn52OyAkw\nKwYDVR0RBCQwIoEPZm9vQGV4YW1wbGUuY29tgQ9iYXJAZXhhbXBsZS5jb20wDQYJ\nKoZIhvcNAQELBQADggEBAJbnKvwqVhaA4tN5DEbbw4ir0+85Zkupq2wOMAgHfPwD\nbPfd+z6oyGgoq05zl4AnXcWdUgCqCCXI+dzfZHOkWFu9XxpTpDOjsUU4Lb7X86TE\n9HoHcUTxomUC5HGEAbWDS96Dta2suTwXQgyafet/qybdmzr9lTdVzAHDPyDf5e1J\nUXpC6vOKP9puwRoRuUVNbskh9ONPMXJbuwGStn/xip5s0H+W1+spCVM4JkEA8jME\nd72p7mCeBrd9Jq4cT1a9pbZQQL6+hCpUIVlHfaUeY20oNk2m5GJpm5v6K0joZNcU\n9GKiJhelBVhKONJE5zOQucGMhQKZuAMaA9LPrKVrRJg=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "t/cert/mtls_client.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAvlsJTJRx04JUSkJqdqo0XSjZReZEmnSfpuZ4SZ7GIHUyX5I7\n7G5Le7B1HHUJAAV31lnKVVsTtnY6xhjcN2ogk+YmVl0LlowB8pY4CAg2omQSIaCN\nSM2aJniSKbZj6xTZtuWH99VVpMxTHKN8uL2tfKTUhh+nHEPFGrXxA73+GZgdtxMr\nk6IqDiF+Qqm7KGlJWeeJDn1azvvUDCBq4duyauWnVeDQWEriCHiCuQYMZfkkBuaK\nE7Ka7xtKsjq0mH/dPA6FC6bGRy9jwnNSQdt8BsMqtS3R4TDVxHnJuTVoRq3ERVfq\nEYgnN+2sSS3E1samdI3TvODZaSUMDLDjt8uNmQIDAQABAoIBACqRsUKu78WdH7x7\nndNrvMoYmH5JQI5KBmoMoFnWZ/haPSmiSkRVZgwDKi1y/tBCaMpGyjjMZVwolHw4\nkwbRdPeeQHSP2keQh974OQ+SxqUKPAPJI89kK1TvIcCySSYJQ6bjLcT+sGhqSSve\nY8XspR96vQxBh92KSknu5jcwBeMy/eG0mmszzP3y2R0BPztuZdE6dq/KxWQ/R4/P\nJG9V1rNkIY+1JZvIICIH1Ehn4UKjiE+FJmyDbDlPKEi7W4CpRnShMLOF4cCFnQLW\nRQds3Dj9GcVY+8Q/GLZF0ATjekIyEsKZEgrMAUF5ZSGRpjJQEHX7oseAiQGQxtHT\nnj5b1AECgYEAwewXbbd1MqRQ6ohfsQ8j5HSMY6ahvUzs1dZUckr2jw8B98tfi/uj\na6Jq1KZe12+4dfwruRSaYdTsSVuvNiSJOxElY0C1p+lXdprFf7XfoQ6UNtg22jcH\n9f8cftnlJoV5whh3YKjqnnnAWUQZ61FTNJ258/t+x0ZgpBJvqBoHwDUCgYEA+0qp\nFZ5xS4FLJMc+Xf/hUeXo+04e4OD/se3atYqyuh1ghmQZfRRPOC110HG99H+rzq/x\nxPMvRFahkAMyi+/3oIcBEuXvoQyqscIsAhkWD/e9t3Qc9OsWe1hlAgWKZxr6oR2U\nKKR1FD7UVecOH+FKCKaL5UpEt4yEigc1NtSlTFUCgYBnV5agrIyzQSex5J0CMWxS\nOd362PkGdXEc/8we4F4GnNvSnrm7Uo2jNXmy+zo9mtb1YT43sogXLK4C5e44bz4G\nkTuYagqkgdBPb2lihpy3KprHo2+P2JXQfXRFEX9xiN37Fqi/hSUK8R0VNRqO8dbi\nik9nexXzwkiMBxsjvUN2JQKBgFy62FpZ9YTfWVNhEuqtGgCWzrqtwUdKwBBwrVyA\nqiNz48Kz/ZPigrlATVF2J5qp4kSLOLRs6OxW65exFl39V2utZgALSbosanDeLk83\n4qRRz3h7KJRYjBtIKz3rvX7+va3mtF2rEmk+Jizs7pFlGWTH0Kf0GBeDiwVEU6bA\nIZ9hAoGAQTjnRGMjvyhq0aPYP+mRFiMKSkcL1nyXizYInfAnbfbL/uEODH7D+iMf\nkak+UgmeD9ce5d/APmZp3/FzYH/M8ivBgG+MnaI+MLVMhmQdLZyMtbSKKaDpiim7\nDdN1wCXYbur0HlO2t+wemMZPpQu7wybgEOLlIG7Yj/0OWDcal1c=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "t/cert/mtls_server.crt",
    "content": "Certificate:\n    Data:\n        Version: 3 (0x2)\n        Serial Number:\n            2f:d5:41:13:5a:ff:c7:1c:5b:ce:28:cd:a6:f7:a5:5a:0d:c0:e2:d2\n        Signature Algorithm: sha256WithRSAEncryption\n        Issuer: C = US, ST = California, O = OpenResty, CN = OpenResty Testing Root CA\n        Validity\n            Not Before: Mar 13 15:49:00 2022 GMT\n            Not After : Mar  8 14:49:00 2042 GMT\n        Subject: C = US, ST = California, O = OpenResty, CN = example.com\n        Subject Public Key Info:\n            Public Key Algorithm: rsaEncryption\n                RSA Public-Key: (2048 bit)\n                Modulus:\n                    00:d7:03:80:a7:42:7d:06:5a:7b:70:d8:11:96:dd:\n                    63:35:53:07:28:71:52:05:40:55:83:61:a7:14:ac:\n                    cf:4b:9b:ab:b7:4e:9d:79:e9:13:3d:bc:c3:67:8f:\n                    dd:88:d9:8b:c2:31:aa:b8:28:9e:13:70:db:76:b0:\n                    12:1c:f8:35:c6:2e:33:9c:b9:04:e3:47:e0:f9:e4:\n                    7f:a5:55:03:0c:2d:b2:54:17:29:12:dd:61:6e:5c:\n                    33:9f:e5:8f:8a:2b:41:53:dc:e1:98:49:63:df:e3:\n                    00:30:2d:1b:bb:f0:8f:cb:04:ec:c9:98:c4:09:5b:\n                    b4:ba:a9:a0:0a:77:d2:42:76:7c:ac:64:c3:97:85:\n                    50:5d:7d:02:61:2a:00:93:d0:69:5e:87:22:f0:c1:\n                    1e:53:46:02:40:37:c9:55:77:99:7d:9d:3d:35:14:\n                    74:84:e3:73:ca:e7:4a:ab:33:98:26:aa:41:4b:b5:\n                    e6:63:7c:a4:1e:25:6a:88:f4:56:d9:2c:63:dd:89:\n                    19:fa:25:41:44:95:87:40:a7:9b:4e:3a:91:29:32:\n                    79:66:05:f4:2f:68:2c:06:53:df:4d:60:be:ac:09:\n                    20:61:9c:6f:1a:a6:07:5a:e7:41:91:9d:36:77:38:\n                    18:3a:69:7b:67:29:9f:1d:e0:c2:d2:8f:16:5b:14:\n                    e8:e1\n                Exponent: 65537 (0x10001)\n        X509v3 extensions:\n            X509v3 Key Usage: critical\n                Digital Signature, Key Encipherment\n            X509v3 Extended Key Usage: \n                TLS Web Server Authentication\n            X509v3 Basic Constraints: critical\n                CA:FALSE\n            X509v3 Subject Key Identifier: \n                16:07:B5:C2:4C:B5:2D:4F:B8:E9:D6:FA:2F:3F:C0:1B:B6:4F:20:E6\n            X509v3 Authority Key Identifier: \n                keyid:F0:D7:4B:14:73:E1:67:00:6B:54:B4:19:20:76:12:9F:9D:8E:C8:09\n\n            X509v3 Subject Alternative Name: \n                DNS:example.com\n    Signature Algorithm: sha256WithRSAEncryption\n         d9:c0:c0:d6:8b:44:04:26:b3:98:24:2c:12:82:6d:15:79:92:\n         76:c9:77:94:c1:be:8f:8a:18:78:96:04:68:c9:0a:d1:84:c5:\n         de:cd:ba:b5:a2:3b:d4:0a:70:be:00:49:19:c0:6e:ca:e9:e5:\n         8b:b6:e3:a2:39:0d:d8:ee:55:1a:08:73:39:19:d3:07:07:33:\n         8c:d8:1b:0f:1b:73:0e:84:72:cf:e6:c1:a1:da:39:aa:c0:2e:\n         3d:b9:a6:8f:ec:98:3a:07:58:34:c2:5e:4c:1a:6b:db:ce:51:\n         92:25:1d:ba:78:4b:11:b6:f1:69:02:cb:ac:32:bb:80:f9:15:\n         91:bf:4e:6a:ab:51:51:7c:7b:1a:72:80:96:eb:0c:fa:56:0e:\n         f2:87:3c:16:8a:04:aa:8a:9d:0c:d9:e0:c4:2a:20:42:5a:12:\n         41:52:30:50:3d:85:f8:07:31:6b:af:a4:d2:44:38:69:ab:88:\n         05:d4:5b:68:34:02:dc:99:5a:6c:b7:ea:fc:79:76:fe:68:29:\n         df:94:22:58:46:f2:40:cb:e1:92:17:d8:1e:3d:fa:a2:56:4f:\n         ac:3c:3d:ae:f7:90:12:ac:3b:6c:1e:1f:26:48:08:87:9a:0e:\n         8d:9d:75:ef:86:1e:63:ac:e9:14:47:ad:3f:4f:10:57:2a:d1:\n         95:ec:6f:24\n-----BEGIN CERTIFICATE-----\nMIIDwzCCAqugAwIBAgIUL9VBE1r/xxxbzijNpvelWg3A4tIwDQYJKoZIhvcNAQEL\nBQAwWjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAoT\nCU9wZW5SZXN0eTEiMCAGA1UEAxMZT3BlblJlc3R5IFRlc3RpbmcgUm9vdCBDQTAe\nFw0yMjAzMTMxNTQ5MDBaFw00MjAzMDgxNDQ5MDBaMEwxCzAJBgNVBAYTAlVTMRMw\nEQYDVQQIEwpDYWxpZm9ybmlhMRIwEAYDVQQKEwlPcGVuUmVzdHkxFDASBgNVBAMT\nC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1wOA\np0J9Blp7cNgRlt1jNVMHKHFSBUBVg2GnFKzPS5urt06deekTPbzDZ4/diNmLwjGq\nuCieE3DbdrASHPg1xi4znLkE40fg+eR/pVUDDC2yVBcpEt1hblwzn+WPiitBU9zh\nmElj3+MAMC0bu/CPywTsyZjECVu0uqmgCnfSQnZ8rGTDl4VQXX0CYSoAk9BpXoci\n8MEeU0YCQDfJVXeZfZ09NRR0hONzyudKqzOYJqpBS7XmY3ykHiVqiPRW2Sxj3YkZ\n+iVBRJWHQKebTjqRKTJ5ZgX0L2gsBlPfTWC+rAkgYZxvGqYHWudBkZ02dzgYOml7\nZymfHeDC0o8WWxTo4QIDAQABo4GOMIGLMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE\nDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQWB7XCTLUtT7jp\n1vovP8Abtk8g5jAfBgNVHSMEGDAWgBTw10sUc+FnAGtUtBkgdhKfnY7ICTAWBgNV\nHREEDzANggtleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEA2cDA1otEBCaz\nmCQsEoJtFXmSdsl3lMG+j4oYeJYEaMkK0YTF3s26taI71ApwvgBJGcBuyunli7bj\nojkN2O5VGghzORnTBwczjNgbDxtzDoRyz+bBodo5qsAuPbmmj+yYOgdYNMJeTBpr\n285RkiUdunhLEbbxaQLLrDK7gPkVkb9OaqtRUXx7GnKAlusM+lYO8oc8FooEqoqd\nDNngxCogQloSQVIwUD2F+Acxa6+k0kQ4aauIBdRbaDQC3JlabLfq/Hl2/mgp35Qi\nWEbyQMvhkhfYHj36olZPrDw9rveQEqw7bB4fJkgIh5oOjZ1174YeY6zpFEetP08Q\nVyrRlexvJA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "t/cert/mtls_server.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA1wOAp0J9Blp7cNgRlt1jNVMHKHFSBUBVg2GnFKzPS5urt06d\neekTPbzDZ4/diNmLwjGquCieE3DbdrASHPg1xi4znLkE40fg+eR/pVUDDC2yVBcp\nEt1hblwzn+WPiitBU9zhmElj3+MAMC0bu/CPywTsyZjECVu0uqmgCnfSQnZ8rGTD\nl4VQXX0CYSoAk9BpXoci8MEeU0YCQDfJVXeZfZ09NRR0hONzyudKqzOYJqpBS7Xm\nY3ykHiVqiPRW2Sxj3YkZ+iVBRJWHQKebTjqRKTJ5ZgX0L2gsBlPfTWC+rAkgYZxv\nGqYHWudBkZ02dzgYOml7ZymfHeDC0o8WWxTo4QIDAQABAoIBAEnmZUiXnJsbbEPr\nr5f3vYptYA9xa2xsoTeHz8JWZuUouwtE1PE6v6c/grXMh6rqgpObOH8VTseFyZhw\nibk1Ql48MPcTzG9FnDinZYvwvRxpdFpcn3xhZIRm4kN5xi0KEuj9CPireM1RmxXz\n2w1scC+qIKxlejNxNpvVgzE136mBqEFKJzecP+yZuH/A86MQCgwqqa3jSz5ApNg+\n1aJE34cGFieDbAN+9sdqWA3OkRrHoy8EakUf4JEvwX1AwUN832mj+N/LfmcCGMeD\nYhzybzlPBV2q2T1+pHIdNT99JVNPkgdTe1903EjnG5oSDGHt2i9MdnNkMsffDWNt\npJiqSHECgYEA2hL6l8Py4oa5AJ2WXriuHRJykAs90K0akftQt4i4lWCbeRhaGh7h\nkPgpDS33RkE4SymVVr0c05abMCKabQBwbu4PNCqetCFtfmIQdQCTUbLbXjL8UuD2\nQnF7nbHiwyGBKRMU/F74oX3z7lXLgRtIiyyo5yYgIAQqpz3oJAaXNTUCgYEA/GhE\nZiez8FXVAg3XwwrE3SexRFKv1JqipYE4mr+ouzfpn9yn8mttxbOORiAAEBl3ZPhd\nZUBzLy19fdFZ8RJ0zPsqoZxsd09/XetaBU56C/g9u0fycj1L2elh9rQAlOW0Grus\nl8jBh01TGtlg0xobK0zjwdGPcbYkp1IzIqyD9n0CgYEAicBvVyrJ5FnhxwfEkrTq\nFycuAtt3Arg2DnzH8geFQaayzv2Y/OMA7Yg0tkSQ7GoKW0A7O31eFjIOeYuCLNSY\nMRpjtDov4e0zsx/S8XWZmYP3mjtutBOyuyngQi655TTm18FcAkcjmy9qxOShFj7b\nxj5BuzGUHWVEZDxwxUD8hvkCgYBnrcyqyZQ4HImqllUSYNIMpclC71QaWIqGwVWm\n+yMsBAOLDvBNu6MTmnXOiEZ+VnecmgiDFr45ms35aI0xYQtpR6JzT/Wd7KG8ynfn\nxhyL3iQ9UYhdNKB7mkoLNFUo1FHuyThUALq+AR0p4jDLheWzG5pSeuoZI2Ba+oDW\ntVZfYQKBgC5phtERR5LKU5Wkzm+uY2j+Nzh4kuKkdLosB9pUW8VnrwFDLZ+r1CxG\nL6CxOZ0AylCMIlrFeUXMa91kLDJYch0NUPHuGBkdIBDXi2kqN7GflTdV3Z8uev20\nuMjErA93yVOWHTR3Wo8WIHy5mdsNRQgGAPw1RVW7rnYIyXJW/mTs\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "t/cert/test.crl",
    "content": "-----BEGIN X509 CRL-----\nMIICGzCCAQMCAQEwDQYJKoZIhvcNAQELBQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYD\nVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRIwEAYDVQQK\nDAlPcGVuUmVzdHkxEjAQBgNVBAsMCU9wZW5SZXN0eTERMA8GA1UEAwwIdGVzdC5j\nb20xIDAeBgkqhkiG9w0BCQEWEWFnZW50emhAZ21haWwuY29tFw0yMzEwMDYwNjM4\nMzlaFw0zMTEyMjMwNjM4MzlaMCcwJQIUImcuJ8MJpeNhvYBEoEGGz5oCBg4XDTIz\nMTAwNjA2MjkyNVqgDjAMMAoGA1UdFAQDAgEEMA0GCSqGSIb3DQEBCwUAA4IBAQBj\nFciorrAuXxn1ULW0XJ7PElwTxZtBrb838EHYvkZ5OdT5tZcucYR6XTZpfT1Up/Px\nrC9EZ1/aZ0wSQfYEQuctafyVCJoPN71IV9IWpPTWm8JyEvnE1W3SgHJujItanyZ3\naMDihljxV9eKyEQnZPQZkaOjAKhj8d2/XZLQ3uIrjWy+/OxcaBQK4a8bDoSM3GZT\nJ6YCD2UBVYKSiROMZZAj3m1thLAGm1RM7A6vjEcH7rPyoxhok5SdxXH5ERY94/ro\nMcjNCv6zV8/Ue5/+ajz5pu/48T901mlIicHry8uImJvlnqAXlH8ReJ+hiSfGhbZ5\nWYNf0wN81NXwTxPIb+v2\n-----END X509 CRL-----\n"
  },
  {
    "path": "t/cert/test.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDtzCCAp8CFCJnLifDCaXjYb2ARKBBhs+aAgYOMA0GCSqGSIb3DQEBCwUAMIGX\nMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2Fu\nIEZyYW5jaXNjbzESMBAGA1UECgwJT3BlblJlc3R5MRIwEAYDVQQLDAlPcGVuUmVz\ndHkxETAPBgNVBAMMCHRlc3QuY29tMSAwHgYJKoZIhvcNAQkBFhFhZ2VudHpoQGdt\nYWlsLmNvbTAeFw0yMzA5MDUwNDE5MjhaFw0zMzA5MDIwNDE5MjhaMIGXMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j\naXNjbzESMBAGA1UECgwJT3BlblJlc3R5MRIwEAYDVQQLDAlPcGVuUmVzdHkxETAP\nBgNVBAMMCHRlc3QuY29tMSAwHgYJKoZIhvcNAQkBFhFhZ2VudHpoQGdtYWlsLmNv\nbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJzRMFoLDuYOwJ8szrS4\nnOibtiimiXZJGx3I/RcFZxaH4nL/WcEb1fwftMQxx73IBJnqnDYkDOzUmzItPMn0\nt2WrNYesC5GqLNRm87m6PVt010tZvq/WxTn6+9qruiGm1PhFxzLQfrClpEeOshlG\nUeoQjPOMrhCmofDM2NQo3D4wIQT0kCJxIPq6wCZt22/Yqz1EmR0UnF/R3ZtiB8O+\nSQGcsUKy4se3919xq+ZkzBdMxLneO5sofUiDC9MgRfiU960tbHPGX9I9P+kLK89S\nyajPEYaRUkSBFjV5kdDK3+L6XckdMbY2pvwhAnVXSmd13Bf2V9XisUrX2Mr4YlnS\nsy0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAVPY/z6Mvjg5EGHzU8bXyuXqxrx8Q\nGBwf3PY25aDF6ofRrTCzMdIhthv8eRtGwHinkpgaK34D7hI/dPB7aswQTzED5c+l\nS2au5OzzCj454oXdhSRA5Rt0mu/+pxmQ+iNk+7XJxgTN0mk1dYQqodyZ+vC4NIYb\njavMlU4zDm4JPtwDs0Mz/d7gf14MU60jppF2vl6AYFHKYBLMHBmqxjy6H9YHjRjQ\noe4TNpn0zxJAPu5LqMkfB2+eLOe6ced7DcLLbbeVJ4Xtqj6Y5KsAyVojWQxrk4vW\n3WO/953pHofO5F2ricS/rsf+5ivTmfiP8mQYTtp7k3T11sIZ4DOmtNwO4A==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "t/cert/test.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCc0TBaCw7mDsCf\nLM60uJzom7Yopol2SRsdyP0XBWcWh+Jy/1nBG9X8H7TEMce9yASZ6pw2JAzs1Jsy\nLTzJ9LdlqzWHrAuRqizUZvO5uj1bdNdLWb6v1sU5+vvaq7ohptT4Rccy0H6wpaRH\njrIZRlHqEIzzjK4QpqHwzNjUKNw+MCEE9JAicSD6usAmbdtv2Ks9RJkdFJxf0d2b\nYgfDvkkBnLFCsuLHt/dfcavmZMwXTMS53jubKH1IgwvTIEX4lPetLWxzxl/SPT/p\nCyvPUsmozxGGkVJEgRY1eZHQyt/i+l3JHTG2Nqb8IQJ1V0pnddwX9lfV4rFK19jK\n+GJZ0rMtAgMBAAECggEABjaOkcllis1o/yrVZMPPabLpAHV6tZ5MuKfNiUOMSPr+\nHfF1OFQL7MxCdfyFQ1prqOp/9nAut+puMgp99wAfDQ7qanNGq7vgQKkfPSD+dy4V\nrUquELBJH6nh9SZqfpSqKaJgHlNe6vehHuRYikJRkrJwVzegGjuekm3B+y6Zl/gc\ne0p5Ha3MTLTFjocwYzgTjJlxD40wlbjpuVnmzKjo8AKNv1F1azMaqBmt1VfPiDn0\nXyq4SPEsWKnEAl2kZdaIBR6zIx7Z3zNUwkfb32QwNoSyo8wS7lCgf2GVS7r1Eul6\niiCE/Gd7w10alW4Pu96shVqkvKn7ROF2nBP9xOSPwQKBgQDCuD6mlNpA07iOX364\naAzIAYookceVA0I9L/fbOQW7RgpvYpM8lxr31TQ3fBDkXSgjzMMYjnk4kz+xN+BB\nWFdjb4raUBtrvip8Q8QZ53DVQK/LodHh0XhipbOxZrDm+6o5nQD0fTqHCBIHSVFF\ntXX2Y90t1cxWMMleRhfNEuzkQQKBgQDOK0rs7mf04Xhc4ZIRIxOtNFnthGp4Kqp7\nSD8VQpbPOLV8iqZEtXIy/hvoTpfQW30c1931KgDQ3Pv5MZYpI7PLqrqkj4tGCQ91\nDJ03GWkSXcMwlPmJRbvgWIeCLgShU5PLxmQu3mH2DP+uGFUBq5/6miDDVjF9z6vb\nBwYlG66j7QKBgA0n/bOrowN2SqXz9c/n19U7pWYQU3fR/Iu9zfVV6Pk6RkI4WtJh\nM0VDdn+5Njr3wFqK3zOtjKsx57/FkrVXjq/9PVh6yR+CfcRfn8RQSuNdt4L+r/ud\n95BSuc1mrtUsc9for8PVIjs1ZGJxpbgcBphbLvqF04SPT0u7WKhWewMBAoGAcJO/\nRAUiitsbaExcADORKQDvIf0uThOuJ8dZevhzdQ/YOftTsy0JAMM05fMUfteWR8uw\nDZE0BNjGVlo3TpuKL+o4JGele0azRAzxRAcCEt9UGBEg+U40utpclD8glB8ZEypv\nxg/0mfCbJKtwr4rRvnuu7DsCp1pg0ybQui6VfDkCgYBXHwcrZwmv7kgr4pUG6oZj\nfzjFenQFqibvb2h7QESyCW13O885GxU13DKv4zg1yi6EqPIopz16qCiUNCvWr5Us\n6sI74wEVI3MzmzG0Htgl29q5yWpeY+7libC/fbZYG8GFgdINq58ko9be1u/8644S\nt2hoKM9/vrVFh9p9qGzckg==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "t/cert/test2.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIID7zCCAtegAwIBAgIUS1f/CoGJaRaJvBgrXEXeTSm7kM4wDQYJKoZIhvcNAQEL\nBQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH\nDA1TYW4gRnJhbmNpc2NvMRIwEAYDVQQKDAlPcGVuUmVzdHkxEjAQBgNVBAMMCXRl\nc3QyLmNvbTEiMCAGCSqGSIb3DQEJARYTb3BlbnJlc3R5QGdtYWlsLmNvbTAeFw0y\nNDAzMjQxMDI1NDhaFw0zNDAzMjIxMDI1NDhaMIGGMQswCQYDVQQGEwJVUzETMBEG\nA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzESMBAGA1UE\nCgwJT3BlblJlc3R5MRIwEAYDVQQDDAl0ZXN0Mi5jb20xIjAgBgkqhkiG9w0BCQEW\nE29wZW5yZXN0eUBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\nAoIBAQChXBMRP5X9Y0l5NogyIZatou1YDT2BDE9tza6dYKUdraJlGo1T4DZf7LoD\nmKFvIpVqLedppZ9z9SrlrsZ8Be6OUbw3kxSyUPDvpauI4XXBR2ZuAHIzTy/Uare1\n1iZSjbT0KATiWviMWCWWblyBU5QPMFJJOmcjRDAZocfIUs4CzglRaDorl5qUFJHr\nmROaIsag/10nnClTwaGwj1pHmFitKRHTIrYICYd5wGgstte9mDFz15s9a3f/Dwhc\n/7neEHYaryRVhR627aDGVHHpcMRWr+FC8QmZTTkiFmO80eeaiyGGTS0AEW51EPI9\nNr142PEaQETpjQaQnfNhZGnwTZHTAgMBAAGjUzBRMB0GA1UdDgQWBBQb73HlJHF7\nB4a1PWRkb3aovk7EtDAfBgNVHSMEGDAWgBQb73HlJHF7B4a1PWRkb3aovk7EtDAP\nBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCQJcLh7+NzAukTG4hV\nnVd572U4sCnlXwTN4lnRIkSGbQxOWwbuI7p05iAT9HJrFLm+rV0EOzxyl/fYkPgR\nDKYfj7sGPp4Lo0NyQ3xhoVypqDDmv7Wm9CLy2mVTJ07khW5tV8GHg9mFwgb/XBTQ\nc+RnNalIapqdlkQbxms5HLzKzTVRuDt+wvqcAsiKFwKDAL9DFrp36TP1t023w+vd\nVb6Ms+l+xFFcxMCxFEOkSPc7szwSce9Q+Hk2C6JdCU30gJPyEBUMl6vD+0l0IFG0\nZEw1BhBp4d92xHiYgdnDEH1RdLPI0q1r7LJL+5ic+VFWycgbC0KRHCarduYgvMYy\nGbRG\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "t/cert/test2.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQChXBMRP5X9Y0l5\nNogyIZatou1YDT2BDE9tza6dYKUdraJlGo1T4DZf7LoDmKFvIpVqLedppZ9z9Srl\nrsZ8Be6OUbw3kxSyUPDvpauI4XXBR2ZuAHIzTy/Uare11iZSjbT0KATiWviMWCWW\nblyBU5QPMFJJOmcjRDAZocfIUs4CzglRaDorl5qUFJHrmROaIsag/10nnClTwaGw\nj1pHmFitKRHTIrYICYd5wGgstte9mDFz15s9a3f/Dwhc/7neEHYaryRVhR627aDG\nVHHpcMRWr+FC8QmZTTkiFmO80eeaiyGGTS0AEW51EPI9Nr142PEaQETpjQaQnfNh\nZGnwTZHTAgMBAAECggEAAhemuvqx8A25YQQsgvM9jXXckbTmPxtQ7QcVmb6BlZ9v\n834DJtB0VA0b97pNyZpPXrJGsG+F4E6QZlcRsCpcMWXs72Q4Xfw/tj6AjsSsWWZw\nH8K2Dbqb+lYB7i6J463TvWDGzhqeOM+seRq9l1hlRfHQogN1spbf8CJ8Fo8VqHf3\nOyId5db0nRUFYV9ciPvJSnk8xi+0aJ2LWVAkCaXQkntbI3529x8KPQm9bGUjdMgo\nA+HXAKZTZL7GtGZoSU4jjyymGLM4hPtzXBExbkX7s7HOP1AeWGom4reOC6XNScQq\nysfs0wVzJAVSl0vFbQfUT6GZ2ZsaTdkT+JgXrYwp5QKBgQDIzY1O/Hj2koVgfKmS\n2+1JVehX8+yjgz3rSNVUauISLoMaYN4hipsKb7Vi6aDFl+Db296Qql7kIzzd/nFk\nUwdBjShUi/xbewGcrYEXbdcXSIxt9ius76eJ9INfa4EmLrZoiX/3Am1u+CcdtcKm\njkqy1o9zoIPbD6lc242zGFK2nQKBgQDNtukEZop/PngCX0yDGWLxisrvFYSrHxnW\nx9/Fw0B56qmyG2FpFg5WDlErenHXh+KSlQ3pV1sWK/+3pVzgGP2ZK+nv3ThEQJmg\nIbI32dABmUG9+HckxgOuAzfqGW6Y4m5I8YpODjn26V6GeS+xQj0bolr4lZ00QSEG\nR5BxxLLHLwKBgQDAzYzWwFgs+HaUNF5lokFt3syeVplqTsOPPHmI+q9iocJD+6qN\nLha7qJLTDFvQHYpL0AsdgFhoWOVCieK6X3ZiHHUS6O4/sBXWKEoBAvg5ZPFhS7KX\n8+w768iQQBrbXJLMQOnbdDs7B44XWsQxRAK5QIawkPjJx3norO6bfck9iQKBgA05\nZe9ffCXD6UkVwUBoQGEQsA0AkB/EBxA0lyEKdTmhKTmG4xMzVMaZcwRdgbX+SUVt\nCZDnibZ6K50tpzPMx3iyRv6hdP2GPZn6sI9AlEuWA5DnyRj3FcN49344FlEDtV1B\nEcgEyBskU2xHnBZEENOW80wpqgpy6WGS9ikqIOgHAoGAeAUetHWIdZZ+Qt6SS1s2\nR17YAiZe88QFAo3uK/dyApLgY7wvbM0n7Cor/3tbpzpzAujUb/qNqTdTXsZIeRfq\nbI767g4/pNl7+jhDUKmDI001YdfHvcRSwjzyjshuSinJY9rydXLUIEWtkTG3f9v8\nd+yLXD2Dz9gF6l2Msj+C3To=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "t/cert/test_ecdsa.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBtDCCAVoCCQD0QJnL8zpA0jAKBggqhkjOPQQDAjBhMQswCQYDVQQGEwJVUzET\nMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzESMBAG\nA1UECgwJT3BlblJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0xNTA3MTcyMTUx\nNDFaGA8yMTE1MDYyMzIxNTE0MVowYTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh\nbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoMCU9wZW5S\nZXN0eTERMA8GA1UEAwwIdGVzdC5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC\nAAT/OtGmlIlbtvvJ3OP0dm5lyEMCrMnpDTDjwBPnUZ2f+16LCmNsdtEJ0r0Sd4GM\no4Lss2JpwzPy2SLGEj3KwGKSMAoGCCqGSM49BAMCA0gAMEUCIQDbNwDkq1FiqcRD\nXdbP1MPAc33N2IK9EDIfMgJ0nTL82wIgNZiL4xvCQe9UA0zC+JqHLnVCQHYAM9kI\nBbvzNrt0hEM=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "t/cert/test_ecdsa.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg0vwBPGgv1hE6RnQo\n3imyoceR+5dLsKegodOlBwnWtbuhRANCAAT/OtGmlIlbtvvJ3OP0dm5lyEMCrMnp\nDTDjwBPnUZ2f+16LCmNsdtEJ0r0Sd4GMo4Lss2JpwzPy2SLGEj3KwGKS\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "t/cert/test_passphrase.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEDTCCAvWgAwIBAgIUZn0PL6eNqoKWughHd0SFO4aE/UswDQYJKoZIhvcNAQEL\nBQAwgZQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ2RvbmcxETAPBgNVBAcM\nCFNoZW5aaGVuMRIwEAYDVQQKDAlPcGVuUmVzdHkxEjAQBgNVBAsMCU9wZW5SZXN0\neTERMA8GA1UEAwwIdGVzdC5jb20xIzAhBgkqhkiG9w0BCQEWFGd1YW5nbGlubHZA\nZ21haWwuY29tMCAXDTI1MDMwNDAxNDkwNFoYDzIxNTkwNTAxMDE0OTA0WjCBlDEL\nMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5nZG9uZzERMA8GA1UEBwwIU2hlblpo\nZW4xEjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYD\nVQQDDAh0ZXN0LmNvbTEjMCEGCSqGSIb3DQEJARYUZ3VhbmdsaW5sdkBnbWFpbC5j\nb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmsvo9t9/lhfavK6S8\n7lqP8lfpV1gZE4ahyRCQI0vgQitlhGjEzvBc7qTtkunQpSWkuHSczOR1WuKzO+PG\nubH0gjeyvEE2X/cipQ1kjiO9Mv8w2qOm9BkC9Wrob01X4mOhGZUgthfwRs0QFSUh\ntzvjg+aApWyIlpmgE/T8bnCwtPS7BuzwEpTt5a51q8JKOr4AMTy64mrMFQRqtcIk\nGYL9CnoxAjYT6NAcEqoapSLDm1xJ7w2RDL1mqkAdXYUQeNPwMivrtb+rCDD4p99A\nTZ8rOuKQzDSxZ3BdCmR5WUXp9z+NeQE5SBu+vkOYMTvEN+e9ApRRnZTO8UHeRB0+\n6vJZAgMBAAGjUzBRMB0GA1UdDgQWBBSmnXGC+KOqNzyGXoujr3TMe3pepDAfBgNV\nHSMEGDAWgBSmnXGC+KOqNzyGXoujr3TMe3pepDAPBgNVHRMBAf8EBTADAQH/MA0G\nCSqGSIb3DQEBCwUAA4IBAQAokEqnbJNDUICvbsRQBFrDBZXjZaEPu69GrTg9OceB\nN9Qu4CKRTL4WsV6uSbdj+Ox/r21UtCI1590oBpC3RiJ+WnGmpdUGVytGIODt/vJ7\n/R03EBy0o9e7XdlR4GfKMC7FVBsP/kkeacR2fswHYXtQrNlyuLhZW04HTDdYNNqb\nLNCnsmwQ4WR4ek9PdcpeFfMY1Up6hTYZ441K8b6xSzMV0/LjzfmHXWoT3U1rQrfM\nesm9t/R3sOvB/wBWQaRe0BbLYpkS7pcq8NQsWvSUFNl5TzrGtUf8KTyvEEdcUcZf\nim48Ea7WYpE3RYs0laLBk/D3sz7vsK5wfiD0R/ASLE+s\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "t/cert/test_passphrase.key",
    "content": "-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQ2v8WxwirSc2M7jaZ\nmrWsPgICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEAlDrk7LLHI/jJqN\nQJ6z8MYEggTQtV86xHehGFn/Qkn5qSZicJ/sDLXQ+m4dFXgw9KMnm0zLG8kzEmb7\nsobUJ4CnwmpBv/GmpPxgy5v1fuvc8HXRJWr7xwv1FoD4lwVOWVFv/qTaII4TfcC6\n3vKqO7MbRaksyI/NmmepGKTGhbzW6XlEdE1TCG7tO02I3puSN0MbJACTpop53TG9\ng+b8HXegPXkLJ2p+ECjUqPwA+h6jZi2pIAztMHQpIizokomrLJ4+2ol3czJpA5nn\nn8JbuYt1XWekq+PBi4l0+lAbk/RiLLK1PCN/Njp7iBBAtNtz9+yJd2Ev7wNDDxKk\ndW73s8uTGde/n6hR4sl/akK8HLaw9tDcWsaHbIMl8zn3WZXksVUfb6CZVOU+BjBa\nyZu3CnP1JTda16HgdZ5dGSNPNbrUeLLIRzM/OxK0yTtmkexOVIgSW0hGE1BXxFXk\nv+mnzrjFrHNRQFhoCFciPqv+WfXsecSASeN0aLWP/RP6CLz6QXWQaK68Dn92v/WP\nxc1NKk8l54wNCLqGaSXadUoTqEdHQH3GV5EkXP7MTb+fYi9/Y/S8wcXJtjUi3ABe\nW+XCOh4zuuQPG7C3i6l/jLIL1ogQNYldad7MoM8eBU8u+TF+NqiJjTLCB1Quycqn\nsN5oVS1VZfsx8XCFsvUAZgxzhjeXViBfwxKSKyT0b1drQZaY58LnyzjmzGkOpRfO\nuzGzWPvHRxkGb9071XaRxD8YDLIngHCw3F2USto88efzmvBfB8ZIxk90S9TZZDeJ\nrqIKpNXsfM4nCR07DW48n3IoiHLCtRFinfpboS5/47ZN4XBE7Q/QNJrWa2P07aWR\nHdOBdD/N3DnM5bbSz7m+5TA+xC4/hEWLXt7Au+vnnhVyDdU80a3+3JY7y4vScHLS\nFn2grTk5F9pqieNhwawpXUCYNGFtJOY8LOQpUZdMpy6n6N5qzSjZ+vgE1Nwr0G/S\n+n8ORUcJzF3s8h+u+SN8OC9W6chnftSIWKiqGsU4oM0FFkr2nOrfNNKqAQo2OU3W\n+5lJovyqAcxtj+4qQsBrYzH7vRzdkigXhb/R7tRDnBRWBpWlMmnUhkaWG8EI0JLs\ngjKNOUFCT1HgTSfonRxScDE5IAg3JxieNz9ppDCphvc0ii3wwdS7uwstKGZCC+Wr\nlQsiV0Wa4WlWVMfLCONI13+zeOli4BmwepgkjJc+YogvLodblcHN14kTTCDvhDMp\nKZglwFT+3zkxH9ArnO7/a8WQCWZV6bJ29HFUfAioE8Sie04WD4zgYEfU02r9A+vq\nwl/w5UK6eXOO6Hz2OrfYh2dZXiCOLp+DhftKq6MKIIAcqFuAzrPvzxd7Uup/fxyX\nn2Ub4YKmUV89RzJ2ipZRjiBcQo+9h4O9dIh4+2I0m7NPALqirutCFBe2cHu/Fwho\nSzacLI21OzWLKbyZ2D6ueH4NrhnOZ10bo/lKbc5d3JzuGv6KW1NmGo32qiKeKNzG\nQ52+sm0/TLiLxy2RlqJ1p7R9NLLZTY8bZTD125n2RtNaJWT564vseY8SaiUW62r8\nksU5aPINa7Qkf4xgsmkSJymFs/9AjXfSCS/aZ13PQMUuI5A4fY9IZjSoiUlSx1bX\n+2B91azW4yfHcAJrluIjZ13LvBU08ooPKPm2TazDi89/8GjBMpHUQN4=\n-----END ENCRYPTED PRIVATE KEY-----\n"
  },
  {
    "path": "t/data/fake-delayed-load-module/config",
    "content": "ngx_addon_name=\"ngx_http_lua_fake_delayed_load_module\"\nHTTP_AUX_FILTER_MODULES=\"$HTTP_AUX_FILTER_MODULES ngx_http_lua_fake_delayed_load_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_lua_fake_delayed_load_module.c\"\n"
  },
  {
    "path": "t/data/fake-delayed-load-module/ngx_http_lua_fake_delayed_load_module.c",
    "content": "/*\n * This fake_delayed_load delayed load module was used to reproduce\n * a bug in ngx_lua's function ngx_http_lua_add_package_preload.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n\n\n#include \"ngx_http_lua_api.h\"\n\n\nstatic ngx_int_t ngx_http_lua_fake_delayed_load_init(ngx_conf_t *cf);\nstatic int ngx_http_lua_fake_delayed_load_preload(lua_State *L);\nstatic int ngx_http_lua_fake_delayed_load_function(lua_State * L);\n\n\nstatic ngx_http_module_t ngx_http_lua_fake_delayed_load_module_ctx = {\n    NULL,                                 /* preconfiguration */\n    ngx_http_lua_fake_delayed_load_init,  /* postconfiguration */\n\n    NULL,                                 /* create main configuration */\n    NULL,                                 /* init main configuration */\n\n    NULL,                                 /* create server configuration */\n    NULL,                                 /* merge server configuration */\n\n    NULL,                                 /* create location configuration */\n    NULL,                                 /* merge location configuration */\n};\n\n/* flow identify module struct */\nngx_module_t  ngx_http_lua_fake_delayed_load_module = {\n    NGX_MODULE_V1,\n    &ngx_http_lua_fake_delayed_load_module_ctx,   /* module context */\n    NULL,                                         /* module directives */\n    NGX_HTTP_MODULE,                              /* module type */\n    NULL,                                         /* init master */\n    NULL,                                         /* init module */\n    NULL,                                         /* init process */\n    NULL,                                         /* init thread */\n    NULL,                                         /* exit thread */\n    NULL,                                         /* exit process */\n    NULL,                                         /* exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\nstatic ngx_int_t\nngx_http_lua_fake_delayed_load_init(ngx_conf_t *cf)\n{\n    ngx_http_lua_add_package_preload(cf, \"ngx.delayed_load\",\n                                     ngx_http_lua_fake_delayed_load_preload);\n    return NGX_OK;\n}\n\n\nstatic int\nngx_http_lua_fake_delayed_load_preload(lua_State *L)\n{\n    lua_createtable(L, 0, 1);\n\n    lua_pushcfunction(L, ngx_http_lua_fake_delayed_load_function);\n    lua_setfield(L, -2, \"get_function\");\n\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_fake_delayed_load_function(lua_State * L)\n{\n    return 0;\n}\n"
  },
  {
    "path": "t/data/fake-module/config",
    "content": "ngx_addon_name=ngx_http_fake_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_fake_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_fake_module.c\"\n"
  },
  {
    "path": "t/data/fake-module/ngx_http_fake_module.c",
    "content": "/*\n * This fake module was used to reproduce a bug in ngx_lua's\n * init_worker_by_lua implementation.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n\n\ntypedef struct {\n    ngx_int_t a;\n} ngx_http_fake_srv_conf_t;\n\n\ntypedef struct {\n    ngx_int_t a;\n} ngx_http_fake_loc_conf_t;\n\n\nstatic void *ngx_http_fake_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_http_fake_merge_srv_conf(ngx_conf_t *cf, void *prev, void *conf);\nstatic void *ngx_http_fake_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_fake_merge_loc_conf(ngx_conf_t *cf, void *prev, void *conf);\n\n\n/* flow identify module configure struct */\nstatic ngx_http_module_t  ngx_http_fake_module_ctx = {\n    NULL,                           /* preconfiguration */\n    NULL,                           /* postconfiguration */\n\n    NULL,                           /* create main configuration */\n    NULL,                           /* init main configuration */\n\n    ngx_http_fake_create_srv_conf,  /* create server configuration */\n    ngx_http_fake_merge_srv_conf,   /* merge server configuration */\n\n    ngx_http_fake_create_loc_conf,  /* create location configuration */\n    ngx_http_fake_merge_loc_conf    /* merge location configuration */\n};\n\n/* flow identify module struct */\nngx_module_t  ngx_http_fake_module = {\n    NGX_MODULE_V1,\n    &ngx_http_fake_module_ctx,      /* module context */\n    NULL,                           /* module directives */\n    NGX_HTTP_MODULE,                /* module type */\n    NULL,                           /* init master */\n    NULL,                           /* init module */\n    NULL,                           /* init process */\n    NULL,                           /* init thread */\n    NULL,                           /* exit thread */\n    NULL,                           /* exit process */\n    NULL,                           /* exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\n/* create server configure */\nstatic void *ngx_http_fake_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_http_fake_srv_conf_t   *fscf;\n\n    fscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fake_srv_conf_t));\n    if (fscf == NULL) {\n        return NULL;\n    }\n\n    return fscf;\n}\n\n\n/* merge server configure */\nstatic char *ngx_http_fake_merge_srv_conf(ngx_conf_t *cf, void *prev, void *conf)\n{\n    ngx_http_fake_srv_conf_t   *fscf;\n\n    fscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_fake_module);\n    if (fscf == NULL) {\n        ngx_conf_log_error(NGX_LOG_ALERT, cf, 0,\n                           \"get module srv conf failed in merge srv conf\");\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\n/* create location configure */\nstatic void *ngx_http_fake_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_fake_loc_conf_t   *flcf;\n\n    flcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fake_loc_conf_t));\n    if (flcf == NULL) {\n        return NULL;\n    }\n\n    return flcf;\n}\n\n\n/* merge location configure */\nstatic char *ngx_http_fake_merge_loc_conf(ngx_conf_t *cf, void *prev, void *conf)\n{\n    ngx_http_fake_loc_conf_t   *flcf;\n\n    flcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_fake_module);\n    if (flcf == NULL) {\n        ngx_conf_log_error(NGX_LOG_ALERT, cf, 0,\n                           \"get module loc conf failed in merge loc conf\");\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "t/data/fake-shm-module/config",
    "content": "ngx_addon_name=ngx_http_lua_shm_fake_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_lua_fake_shm_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_lua_fake_shm_module.c\"\n"
  },
  {
    "path": "t/data/fake-shm-module/ngx_http_lua_fake_shm_module.c",
    "content": "#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n\n\n#include <lua.h>\n#include <lualib.h>\n#include <lauxlib.h>\n\n\n#include \"ngx_http_lua_api.h\"\n\n\nstatic void *ngx_http_lua_fake_shm_create_main_conf(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_lua_fake_shm_init(ngx_conf_t *cf);\n\nstatic char *ngx_http_lua_fake_shm(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_lua_fake_shm_init_zone(ngx_shm_zone_t *shm_zone,\n    void *data);\nstatic int ngx_http_lua_fake_shm_preload(lua_State *L);\nstatic int ngx_http_lua_fake_shm_get_info(lua_State *L);\n\n\ntypedef struct {\n    ngx_array_t     *shm_zones;\n} ngx_http_lua_fake_shm_main_conf_t;\n\n\nstatic ngx_command_t ngx_http_lua_fake_shm_cmds[] = {\n\n    { ngx_string(\"lua_fake_shm\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,\n      ngx_http_lua_fake_shm,\n      0,\n      0,\n      NULL },\n\n    ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_lua_fake_shm_module_ctx = {\n    NULL,                                   /* preconfiguration */\n    ngx_http_lua_fake_shm_init,             /* postconfiguration */\n\n    ngx_http_lua_fake_shm_create_main_conf, /* create main configuration */\n    NULL,                                   /* init main configuration */\n\n    NULL,                                   /* create server configuration */\n    NULL,                                   /* merge server configuration */\n\n    NULL,                                   /* create location configuration */\n    NULL,                                   /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_lua_fake_shm_module = {\n    NGX_MODULE_V1,\n    &ngx_http_lua_fake_shm_module_ctx, /* module context */\n    ngx_http_lua_fake_shm_cmds,        /* module directives */\n    NGX_HTTP_MODULE,                   /* module type */\n    NULL,                              /* init master */\n    NULL,                              /* init module */\n    NULL,                              /* init process */\n    NULL,                              /* init thread */\n    NULL,                              /* exit thread */\n    NULL,                              /* exit process */\n    NULL,                              /* exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\ntypedef struct {\n    ngx_str_t   name;\n    size_t      size;\n    ngx_int_t   isold;\n    ngx_int_t   isinit;\n} ngx_http_lua_fake_shm_ctx_t;\n\n\nstatic void *\nngx_http_lua_fake_shm_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_lua_fake_shm_main_conf_t *lfsmcf;\n\n    lfsmcf = ngx_pcalloc(cf->pool, sizeof(*lfsmcf));\n    if (lfsmcf == NULL) {\n        return NULL;\n    }\n\n    return lfsmcf;\n}\n\n\nstatic char *\nngx_http_lua_fake_shm(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_lua_fake_shm_main_conf_t   *lfsmcf = conf;\n\n    ngx_str_t                   *value, name;\n    ngx_shm_zone_t              *zone;\n    ngx_shm_zone_t             **zp;\n    ngx_http_lua_fake_shm_ctx_t *ctx;\n    ssize_t                      size;\n\n    if (lfsmcf->shm_zones == NULL) {\n        lfsmcf->shm_zones = ngx_palloc(cf->pool, sizeof(ngx_array_t));\n        if (lfsmcf->shm_zones == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_array_init(lfsmcf->shm_zones, cf->pool, 2,\n                           sizeof(ngx_shm_zone_t *))\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    value = cf->args->elts;\n\n    ctx = NULL;\n\n    if (value[1].len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid lua fake_shm name \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    name = value[1];\n\n    size = ngx_parse_size(&value[2]);\n\n    if (size <= 8191) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid lua fake_shm size \\\"%V\\\"\", &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_lua_fake_shm_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx->name = name;\n    ctx->size = size;\n\n    zone = ngx_http_lua_shared_memory_add(cf, &name, (size_t) size,\n                                          &ngx_http_lua_fake_shm_module);\n    if (zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (zone->data) {\n        ctx = zone->data;\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"lua_fake_shm \\\"%V\\\" is already defined as \"\n                           \"\\\"%V\\\"\", &name, &ctx->name);\n        return NGX_CONF_ERROR;\n    }\n\n    zone->init = ngx_http_lua_fake_shm_init_zone;\n    zone->data = ctx;\n\n    zp = ngx_array_push(lfsmcf->shm_zones);\n    if (zp == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *zp = zone;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_fake_shm_init_zone(ngx_shm_zone_t *shm_zone, void *data)\n{\n    ngx_http_lua_fake_shm_ctx_t  *octx = data;\n\n    ngx_http_lua_fake_shm_ctx_t  *ctx;\n\n    ctx = shm_zone->data;\n\n    if (octx) {\n        ctx->isold = 1;\n    }\n\n    ctx->isinit = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_lua_fake_shm_init(ngx_conf_t *cf)\n{\n    ngx_http_lua_add_package_preload(cf, \"fake_shm_zones\",\n                                     ngx_http_lua_fake_shm_preload);\n    return NGX_OK;\n}\n\n\nstatic int\nngx_http_lua_fake_shm_preload(lua_State *L)\n{\n    ngx_http_lua_fake_shm_main_conf_t *lfsmcf;\n    ngx_http_conf_ctx_t               *hmcf_ctx;\n    ngx_cycle_t                       *cycle;\n\n    ngx_uint_t                   i;\n    ngx_shm_zone_t             **zone;\n    ngx_shm_zone_t             **zone_udata;\n\n    cycle = (ngx_cycle_t *) ngx_cycle;\n\n    hmcf_ctx = (ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index];\n    lfsmcf = hmcf_ctx->main_conf[ngx_http_lua_fake_shm_module.ctx_index];\n\n    if (lfsmcf->shm_zones != NULL) {\n        lua_createtable(L, 0, lfsmcf->shm_zones->nelts /* nrec */);\n\n        lua_createtable(L, 0 /* narr */, 2 /* nrec */); /* shared mt */\n\n        lua_pushcfunction(L, ngx_http_lua_fake_shm_get_info);\n        lua_setfield(L, -2, \"get_info\");\n\n        lua_pushvalue(L, -1); /* shared mt mt */\n        lua_setfield(L, -2, \"__index\"); /* shared mt */\n\n        zone = lfsmcf->shm_zones->elts;\n\n        for (i = 0; i < lfsmcf->shm_zones->nelts; i++) {\n            lua_pushlstring(L, (char *) zone[i]->shm.name.data,\n                            zone[i]->shm.name.len);\n\n            /* shared mt key */\n\n            lua_createtable(L, 1 /* narr */, 0 /* nrec */);\n                /* table of zone[i] */\n            zone_udata = lua_newuserdata(L, sizeof(ngx_shm_zone_t *));\n                /* shared mt key ud */\n            *zone_udata = zone[i];\n            lua_rawseti(L, -2, 1); /* {zone[i]} */\n            lua_pushvalue(L, -3); /* shared mt key ud mt */\n            lua_setmetatable(L, -2); /* shared mt key ud */\n            lua_rawset(L, -4); /* shared mt */\n        }\n\n        lua_pop(L, 1); /* shared */\n\n    } else {\n        lua_newtable(L);    /* ngx.shared */\n    }\n\n    return 1;\n}\n\n\nstatic int\nngx_http_lua_fake_shm_get_info(lua_State *L)\n{\n    ngx_int_t                         n;\n    ngx_shm_zone_t                   *zone;\n    ngx_shm_zone_t                  **zone_udata;\n    ngx_http_lua_fake_shm_ctx_t      *ctx;\n\n    n = lua_gettop(L);\n\n    if (n != 1) {\n        return luaL_error(L, \"expecting exactly one arguments, \"\n                          \"but only seen %d\", n);\n    }\n\n    luaL_checktype(L, 1, LUA_TTABLE);\n\n    lua_rawgeti(L, 1, 1);\n    zone_udata = lua_touserdata(L, -1);\n    lua_pop(L, 1);\n\n    if (zone_udata == NULL) {\n        return luaL_error(L, \"bad \\\"zone\\\" argument\");\n    }\n\n    zone = *zone_udata;\n\n    ctx = (ngx_http_lua_fake_shm_ctx_t *) zone->data;\n\n    lua_pushlstring(L, (char *) zone->shm.name.data, zone->shm.name.len);\n    lua_pushnumber(L, zone->shm.size);\n    lua_pushboolean(L, ctx->isinit);\n    lua_pushboolean(L, ctx->isold);\n\n    return 4;\n}\n"
  },
  {
    "path": "t/lib/CRC32.lua",
    "content": "--Copyright (c) 2007-2008 Neil Richardson (nrich@iinet.net.au)\n--\n--Permission is hereby granted, free of charge, to any person obtaining a copy \n--of this software and associated documentation files (the \"Software\"), to deal\n--in the Software without restriction, including without limitation the rights \n--to use, copy, modify, merge, publish, distribute, sublicense, and/or sell \n--copies of the Software, and to permit persons to whom the Software is \n--furnished to do so, subject to the following conditions:\n--\n--The above copyright notice and this permission notice shall be included in all\n--copies or substantial portions of the Software.\n--\n--THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR \n--IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, \n--FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE \n--AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER \n--LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \n--OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS \n--IN THE SOFTWARE.\n\nmodule('CRC32', package.seeall)\n\nlocal max = 2^32 -1\n\nlocal CRC32 = {\n    0,79764919,159529838,222504665,319059676,\n    398814059,445009330,507990021,638119352,\n    583659535,797628118,726387553,890018660,\n    835552979,1015980042,944750013,1276238704,\n    1221641927,1167319070,1095957929,1595256236,\n    1540665371,1452775106,1381403509,1780037320,\n    1859660671,1671105958,1733955601,2031960084,\n    2111593891,1889500026,1952343757,2552477408,\n    2632100695,2443283854,2506133561,2334638140,\n    2414271883,2191915858,2254759653,3190512472,\n    3135915759,3081330742,3009969537,2905550212,\n    2850959411,2762807018,2691435357,3560074640,\n    3505614887,3719321342,3648080713,3342211916,\n    3287746299,3467911202,3396681109,4063920168,\n    4143685023,4223187782,4286162673,3779000052,\n    3858754371,3904687514,3967668269,881225847,\n    809987520,1023691545,969234094,662832811,\n    591600412,771767749,717299826,311336399,\n    374308984,453813921,533576470,25881363,\n    88864420,134795389,214552010,2023205639,\n    2086057648,1897238633,1976864222,1804852699,\n    1867694188,1645340341,1724971778,1587496639,\n    1516133128,1461550545,1406951526,1302016099,\n    1230646740,1142491917,1087903418,2896545431,\n    2825181984,2770861561,2716262478,3215044683,\n    3143675388,3055782693,3001194130,2326604591,\n    2389456536,2200899649,2280525302,2578013683,\n    2640855108,2418763421,2498394922,3769900519,\n    3832873040,3912640137,3992402750,4088425275,\n    4151408268,4197601365,4277358050,3334271071,\n    3263032808,3476998961,3422541446,3585640067,\n    3514407732,3694837229,3640369242,1762451694,\n    1842216281,1619975040,1682949687,2047383090,\n    2127137669,1938468188,2001449195,1325665622,\n    1271206113,1183200824,1111960463,1543535498,\n    1489069629,1434599652,1363369299,622672798,\n    568075817,748617968,677256519,907627842,\n    853037301,1067152940,995781531,51762726,\n    131386257,177728840,240578815,269590778,\n    349224269,429104020,491947555,4046411278,\n    4126034873,4172115296,4234965207,3794477266,\n    3874110821,3953728444,4016571915,3609705398,\n    3555108353,3735388376,3664026991,3290680682,\n    3236090077,3449943556,3378572211,3174993278,\n    3120533705,3032266256,2961025959,2923101090,\n    2868635157,2813903052,2742672763,2604032198,\n    2683796849,2461293480,2524268063,2284983834,\n    2364738477,2175806836,2238787779,1569362073,\n    1498123566,1409854455,1355396672,1317987909,\n    1246755826,1192025387,1137557660,2072149281,\n    2135122070,1912620623,1992383480,1753615357,\n    1816598090,1627664531,1707420964,295390185,\n    358241886,404320391,483945776,43990325,\n    106832002,186451547,266083308,932423249,\n    861060070,1041341759,986742920,613929101,\n    542559546,756411363,701822548,3316196985,\n    3244833742,3425377559,3370778784,3601682597,\n    3530312978,3744426955,3689838204,3819031489,\n    3881883254,3928223919,4007849240,4037393693,\n    4100235434,4180117107,4259748804,2310601993,\n    2373574846,2151335527,2231098320,2596047829,\n    2659030626,2470359227,2550115596,2947551409,\n    2876312838,2788305887,2733848168,3165939309,\n    3094707162,3040238851,2985771188,\n}\n\nlocal function xor(a, b)\n    local calc = 0    \n\n    for i = 32, 0, -1 do\n\tlocal val = 2 ^ i\n\tlocal aa = false\n\tlocal bb = false\n\n\tif a == 0 then\n\t    calc = calc + b\n\t    break\n\tend\n\n\tif b == 0 then\n\t    calc = calc + a\n\t    break\n\tend\n\n\tif a >= val then\n\t    aa = true\n\t    a = a - val\n\tend\n\n\tif b >= val then\n\t    bb = true\n\t    b = b - val\n\tend\n\n\tif not (aa and bb) and (aa or bb) then\n\t    calc = calc + val\n\tend\n    end\n\n    return calc\nend\n\nlocal function lshift(num, left)\n    local res = num * (2 ^ left)\n    return res % (2 ^ 32)\nend\n\nlocal function rshift(num, right)\n    local res = num / (2 ^ right)\n    return math.floor(res)\nend\n\nfunction Hash(str)\n    local count = string.len(tostring(str))\n    local crc = max\n    \n    local i = 1\n    while count > 0 do\n\tlocal byte = string.byte(str, i)\n\n\tcrc = xor(lshift(crc, 8), CRC32[xor(rshift(crc, 24), byte) + 1])\n\n\ti = i + 1\n\tcount = count - 1\n    end\n\n    return crc\nend\n\n\n--\n-- CRC32.lua\n--\n-- A pure Lua implementation of a CRC32 hashing algorithm. Slower than using a C implementation,\n-- but useful having no other dependencies.\n--\n--\n-- Synopsis\n--\n-- require('CRC32')\n--\n-- crchash = CRC32.Hash('a string')\n--\n-- Methods:\n--\n-- hashval = CRC32.Hash(val)\n--    Calculates and returns (as an integer) the CRC32 hash of the parameter 'val'. \n\n"
  },
  {
    "path": "t/lib/Memcached.lua",
    "content": "--Copyright (c) 2006-2008 Neil Richardson (nrich@iinet.net.au)\n--\n--Permission is hereby granted, free of charge, to any person obtaining a copy \n--of this software and associated documentation files (the \"Software\"), to deal\n--in the Software without restriction, including without limitation the rights \n--to use, copy, modify, merge, publish, distribute, sublicense, and/or sell \n--copies of the Software, and to permit persons to whom the Software is \n--furnished to do so, subject to the following conditions:\n--\n--The above copyright notice and this permission notice shall be included in all\n--copies or substantial portions of the Software.\n--\n--THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR \n--IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, \n--FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE \n--AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER \n--LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \n--OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS \n--IN THE SOFTWARE.\n\nmodule('Memcached', package.seeall)\n\nrequire('socket')\nrequire('CRC32')\n\nlocal SERVER_RETRIES = 10\n\nlocal STATS_KEYS = {\n    malloc = true,\n    sizes = true,\n    slabs = true,\n    items = true,\n}\n\nlocal FLAGS = {\n    'STORABLE',\n    'COMPRESSED',\n    'SERIALISED',\n}\n\nlocal function warn(str)\n    io.stderr:write(string.format('Warning: %s\\n', tostring(str)))\nend\n\nlocal function _select_server(cache, key)\n    local server_count = #cache.servers\n\n    local hashfunc = cache.hash or CRC32.Hash\n\n    if server_count == 1 then\n\treturn cache.servers[1].socket\n    else\n\tlocal serverhash = hashfunc(key)\n\n\tfor i = 0, SERVER_RETRIES do\n\t    local index = (serverhash % server_count) + 1\n\t    local server = cache.servers[index].socket\n\n\t    if not server then\n\t\tserverhash = hashfunc(serverhash .. i)\n\t    else\n\t\treturn server\n\t    end\n\tend\n    end\n\n    error('No servers found')\n    return nil\nend\n\nlocal function _retrieve(cache, key, str)\n    local server = _select_server(cache, key)\n\n    server:send(str .. '\\r\\n')\n\n    local function toboolean(value)\n\tif type(value) == 'string' then\n\t    if value == 'true' then\n\t\treturn true\n\t    elseif value == 'false' then\n\t\treturn false \n\t    end\n\tend\n\n\treturn nil\n    end\n\n    local function extract_flags(str)\n\tlocal num = tonumber(str)\n\tlocal flags = {}\n\n\tfor i = table.maxn(FLAGS), 1, -1 do\n\t    local bf = 2 ^ (i - 1)\n\n\t    if num >= bf then\n\t\tflags[FLAGS[i]] = true\n\t\tnum = num - bf\n\t    end\n\tend\n\n\treturn flags\n    end\n\n    local returndata = {}\n    while true do\n\tlocal line, err = server:receive()\n\n\tif line == 'END' then\n\t    break\n\telseif string.sub(line, 1, 5) == 'VALUE' then\n\t    local key,flagstr,size,cas = string.match(line, 'VALUE (%S+) (%d+) (%d+)')\n\t    flags = extract_flags(flagstr)\n\n\t    local data = server:receive(size)\n\n\t    if flags.COMPRESSED and cache.compress_enabled then\n\t\tdata = cache.decompress(data)\n\t    end\n\n            if flags.SERIALISED then\n                returndata[key] = cache.decode(data)\n            else\n                local ldata = tonumber(data) or toboolean(data) \n\n                if ldata == nil then\n                    if data == 'nil' then\n                        returndata[key] = nil\n                    else\n                        returndata[key] = data\n                    end\n                else\n                    returndata[key] = ldata\n                end\n            end\n\tend\n    end\n\n    return returndata\nend\n\nlocal function _send(cache, key, str)\n    local server = _select_server(cache, key)\n\n    server:send(str .. \"\\r\\n\")\n    local line, err = server:receive()\n    \n    if not err then return line end\nend\n\nlocal function _store(cache, op, key, value, expiry)\n    local str\n    local flags = 0\n\n    if type(value) == 'table' then\n\tstr = cache.encode(value)    \n\t-- TODO lookup rather than hard code \n        flags = flags + 4\n    else\n\tstr = tostring(value)\n    end\n\n    if cache.compress_enabled and string.len(str) > cache.compress_threshold then\n\tlocal cstr = cache.compress(str)\n\n\tif string.len(cstr) < (string.len(str) * 0.8) then\n\t    str = cstr\n\n\t    -- TODO lookup rather than hard code \n\t    flags = flags + 2\n\tend\n    end\n\n    local len = string.len(str)\n\n    expiry = expiry or 0\n\n    local cmd = op .. ' ' .. key .. ' ' .. flags .. ' ' .. expiry .. ' ' .. len .. '\\r\\n' .. str\n\n    local res = _send(cache, key, cmd)\n\n    if res ~= 'STORED' then\n\treturn false, res\n    end\n\n    return true\nend\n\nlocal function set(cache, key, value, expiry)\n    return _store(cache, 'set', key, value, expiry)\nend\n\nlocal function add(cache, key, value, expiry)\n    return _store(cache, 'add', key, value, expiry)\nend\n\nlocal function replace(cache, key, value, expiry)\n    return _store(cache, 'replace', key, value, expiry)\nend\n\nlocal function get(cache, key)\n    local dataset = _retrieve(cache, key, 'get ' .. key)\n    return dataset[key]\nend\n\nlocal function delete(cache, key)\n    local res = _send(cache, key, 'delete ' .. key)\n\n    if res == 'NOT_FOUND' then\n\treturn false\n    end\n\n    if res ~= 'DELETED' then\n\treturn false, res\n    end\n\n    return true\nend\n\nlocal function incr(cache, key, val)\n    val = val or 1\n\t\n    local res = _send(cache, key, 'incr ' .. key .. ' ' .. val)\n\n    if res == 'ERROR' or res == 'CLIENT_ERROR' then\n        return false, res\n    end\n\n    return res\nend\n\nlocal function decr(cache, key, val)\n    val = val or 1\n\n    local res = _send(cache, key, 'decr ' .. key .. ' ' .. val)\n\n    if res == 'ERROR' or res == 'CLIENT_ERROR' then\n        return false, res\n    end\n\n    return res\nend\n\nlocal function stats(cache, key)\n    local servers = {}\n\n    key = key or ''\n\n    if string.len(key) > 0 and not STATS_KEYS[key] then\n\terror(string.format(\"Unknown stats key '%s'\", key))\n    end\n\n    for i,server in pairs(cache.servers) do\n\tserver.socket:send('stats ' .. key .. '\\r\\n')\n\n\tlocal stats = {}\n\n\twhile true do\n\t    local line, err = server.socket:receive()\n\n\t    if line == 'END' or line == 'ERROR' then\n\t\tbreak\n\t    end\n\n\t    local k,v = string.match(line, 'STAT (%S+) (%S+)')\n\n\t    if k then\n\t\tstats[k] = v\n\t    end\n\tend\n\n\tservers[server.name] = stats\n    end\n\n    return servers\nend \n\nlocal function get_multi(cache, ...)\n    local dataset = nil\n\n    if table.maxn(cache.servers) > 1 then\n\tdataset = {}\n\n\tfor i,k in ipairs(arg) do\n\t    local data = _retrieve(cache, k, 'get ' .. k)\n\t    dataset[k] = data[k]\n\tend\n    else\n\tlocal keys = table.concat(arg, ' ')\n\tdataset = _retrieve(cache, keys, 'get ' .. keys)\n    end\n\n    return dataset\nend\n\nlocal function flush_all(cache)\n    local success = true\n\n    for i,server in ipairs(cache.servers) do\n\tserver.socket:send('flush_all\\r\\n')\n\tlocal res = assert(server.socket:receive())\n\n\tif res ~= 'OK' then\n\t    success = false\n\tend\n    end\n\n    return success\nend\n\nlocal function disconnect_all(cache)\n    while true do\n\tlocal server = table.remove(cache.servers)\n\n\tif not server then\n\t    break\n\tend\n\n\tserver.socket:close()\n    end    \nend\n\nlocal function set_hash(cache, hashfunc)\n    cache.hash = hashfunc\nend\n\nlocal function set_encode(cache, func)\n    cache.encode = func\nend\n\nlocal function set_decode(cache, func)\n    cache.decode = func\nend\n\nlocal function set_compress(cache, func)\n    cache.compress = func\nend\n\nlocal function set_decompress(cache, func)\n    cache.decompress = func\nend\n\nfunction Connect(hostlist, port)\n    local servers = {}\n\n    if type(hostlist) == 'table' then\n\tfor i,host in pairs(hostlist) do\n\t    local h, p\n\n\t    if type(host) == 'table' then\n\t\th = host[1]\n\t\tp = host[2]\n\t    elseif type(host) == 'string' then\n\t\th = host\n\t    elseif type(host) == 'number' then\n\t\tp = host\n\t\th = nil\n\t    end\n\n\t    if not h then\n\t\th = '127.0.0.1'\n\t    end\n\n\t    if not p then \n\t\tp = 11211\n\t    end\n\n\t    local server = socket.connect(h, p)\n\n\t    if not server then\n\t\twarn('Could not connect to ' .. h .. ':' .. p)\n\t    else\n\t\ttable.insert(servers, {socket = server, name = string.format('%s:%d', h, p)})\n\t    end\n\tend\n    else\n\tlocal address = hostlist\n\n\tif type(address) == 'number' then\n\t    port = address\n\t    address = nil\n\tend\n\n\tif address == nil then\n\t    address = '127.0.0.1'\n\tend\n\n\tif port == nil then\n\t    port = 11211\n\tend\n\n\tlocal server = socket.connect(address, port)\n\n\tif not server then\n\t    warn('Could not connect to ' .. address .. ':' .. port)\n\telse\n\t    servers = {{socket = server, name = string.format('%s:%d', address, port)}}\n\tend\n    end\n\n    if table.maxn(servers) < 1 then\n\terror('No servers available')\n    end\n\n    local cache = {\n\tservers = servers,\n\n\tset_hash = set_hash,\n\tset_encode = set_encode,\n\tset_decode = set_decode,\n\tset_decompress = set_decompress,\n\tset_compress = set_compress,\n\n\tcompress_enabled = false,\n\tenable_compression = function(self, on)\n\t    self.compress_enabled = on\n\tend,\n\n\thash = nil,\n\tencode = function()\n\t    error('No encode function set')\n\tend,\n\n\tdecode = function()\n\t    error('No decode function set')\n\tend,\n\n\tcompress = function()\n\t    error('No compress function set')\n\tend,\n\n\tdecompress = function()\n\t    error('No decompress function set')\n\tend,\n\n\t-- 10K default\n\tcompress_threshold = 10240,\n\tset_compress_threshold = function(self, threshold)\n\t    if threshold == nil then\n\t\tself:enable_compression(false)\n\t    else\n\t\tself.compress_threshold = threshold\n\t    end\n\tend,\n\n\tset = set,\n\tadd = add,\n\treplace = replace,\n\tget = get,\n\tdelete = delete,\n\tincr = incr,\n\tdecr = decr,\n\n\tget_multi = get_multi,\n\tstats = stats,\n\tflush_all = flush_all,\n\tdisconnect_all = disconnect_all,\n    }\n\n    return cache\nend\n\nfunction New(hostlist, port)\n    return Connect(hostlist, port)\nend\n\n-- \n-- Memcached.lua\n-- \n-- A pure Lua implementation of a simple memcached client. 1 or more memcached server(s) are currently supported. Requires the luasocket library.\n-- See http://www.danga.com/memcached/ for more information about memcached.\n--\n--\n--\n-- Synopsis\n--\n-- require('Memcached')\n--\n-- memcache = Memcached.Connect('some.host.com', 11000)\n--    OR\n-- memcache = Memcached.New('some.host.com', 11000)\n--\n-- memcache:set('some_key', 1234)\n-- memcache:add('new_key', 'add new value')\n-- memcache:replace('existing_key', 'replace old value')\n--\n-- cached_data = memcache:get('some_key')\n--\n-- memcache:delete('old_key')\n--\n--\n--\n-- Methods:\n--\n-- memcache = Memcached.Connect()\n--    Connect to memcached server at localhost on port number 11211. \n--\n-- memcache = Memcached.Connect(host[, port])\n--    Connect to memcached server at 'host' on port number 'port'. If port is not provided, port 11211 is used.  \n--\n---memcache = Memcached.Connect(port)\n--    Connect to memcached server at localhost on port number 'port'.\n--\n-- memcache = Memcached.Connect({{'host', port}, 'host', port})  \n--    Connect to multiple memcached servers.\n--\n-- memcache:set(key, value[, expiry])\n--    Unconditionally sets a key to a given value in the memcache. The value for 'expiry' is the expiration\n--    time (default is 0, never expire).\n--     \n-- memcache:add(key, value[, expiry])\n--    Like set, but only stores in memcache if the key doesn't already exist.\n--    \n-- memcache:replace(key, value[, expiry])\n--    Like set, but only stores in memcache if the key already exists. The opposite of add.\n--    \n-- value = memcache:get(key)\n--    Retrieves a key from the memcache. Returns the value or nil\n--    \n-- values = memcache:get_multi(...)\n--    Retrieves multiple keys from the memcache doing just one query.  Returns a table of key/value pairs that were available.\n--    \n-- memcache:delete(key)\n--    Deletes a key. Returns true on deletion, false if the key was not found.\n--    \n-- value = memcache:incr(key[, value])\n--    Sends a command to the server to atomically increment the value for key by value, or by 1 if value is nil. \n--    Returns nil if key doesn't exist on server, otherwise it returns the new value after incrementing. Value should be zero or greater.\n--    \n-- value = memcache:decr(key[, value])\n--    Like incr, but decrements. Unlike incr, underflow is checked and new values are capped at 0. If server value is 1, a decrement of 2 returns 0, not -1.\n--\n-- servers = memcache:stats([key])\n--    Returns a table of statistical data regarding the memcache server(s). Allowed keys are:\n--\t'', 'malloc', 'sizes', 'slabs', 'items'\n--\n--  success = memcache:flush_all()\n--     Runs the memcached \"flush_all\" command on all configured hosts, emptying all their caches. \n--\n--  memcache:disconnect_all()\n--     Closes all cached sockets to all memcached servers.\n--\n--  memcache:set_hash(hashfunc)\n--     Sets a custom hash function for key values. The default is a CRC32 hashing function.\n--     'hashfunc' should be defined receiving a single string parameter and returning a single integer value.\n--\n--  memcache:set_encode(func)\n--     Sets a custom encode function for serialising table values. 'func' should be defined receiving a single\n--     table value and returning a single string value.\n--\n--  memcache:set_decode(func)\n--     Sets a custom decode function for deserialising table values. 'func' should be defined receiving a \n--     single single and returning a single table value\n--\n--  memcache:enable_compression(onflag)\n--     Turns data compression support on or off.\n--\n--  memcache:set_compress_threshold(size)\n--     Set the compression threshold. If the value to be stored is larger than `size' bytes (and compression \n--     is enabled), compress before storing.\n--\n--  memcache:set_compress(func)\n--     Sets a custom data compression function. 'func' should be defined receiving a single string value and\n--     returning a single string value.\n--\n--  memcache:set_decompress(func)\n--     Sets a custom data decompression function. 'func' should be defined receiving a single string value and\n--     returning a single string value.\n"
  },
  {
    "path": "t/lib/Redis.lua",
    "content": "--[[\nCopyright (c) 2009-2011 Daniele Alessandri\n \nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n \nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n \nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n]]\n\nmodule('Redis', package.seeall)\n\nlocal commands, network, request, response = {}, {}, {}, {}\n\nlocal defaults = {\n    host        = '127.0.0.1',\n    port        = 6379,\n    tcp_nodelay = true,\n    path        = nil\n}\n\nlocal protocol = {\n    newline = '\\r\\n',\n    ok      = 'OK',\n    err     = 'ERR',\n    queued  = 'QUEUED',\n    null    = 'nil'\n}\n\nlocal lua_error = error\nlocal function default_error_fn(message, level)\n    lua_error(message, (level or 1) + 1)\nend\n\nlocal function merge_defaults(parameters)\n    if parameters == nil then\n        parameters = {}\n    end\n    for k, v in pairs(defaults) do\n        if parameters[k] == nil then\n            parameters[k] = defaults[k]\n        end\n    end\n    return parameters\nend\n\nlocal function parse_boolean(v)\n    if v == '1' or v == 'true' or v == 'TRUE' then\n        return true\n    elseif v == '0' or v == 'false' or v == 'FALSE' then\n        return false\n    else\n        return nil\n    end\nend\n\nlocal function toboolean(value) return value == 1 end\n\nlocal function fire_and_forget(client, command)\n    -- let's fire and forget! the connection is closed as soon\n    -- as the SHUTDOWN command is received by the server.\n    client.network.write(client, command .. protocol.newline)\n    return false\nend\n\nlocal function zset_range_request(client, command, ...)\n    local args, opts = {...}, { }\n\n    if #args >= 1 and type(args[#args]) == 'table' then\n        local options = table.remove(args, #args)\n        if options.withscores then\n            table.insert(opts, 'WITHSCORES')\n        end\n    end\n\n    for _, v in pairs(opts) do table.insert(args, v) end\n    request.multibulk(client, command, args)\nend\n\nlocal function zset_range_byscore_request(client, command, ...)\n    local args, opts = {...}, { }\n\n    if #args >= 1 and type(args[#args]) == 'table' then\n        local options = table.remove(args, #args)\n        if options.limit then\n            table.insert(opts, 'LIMIT')\n            table.insert(opts, options.limit.offset or options.limit[1])\n            table.insert(opts, options.limit.count or options.limit[2])\n        end\n        if options.withscores then\n            table.insert(opts, 'WITHSCORES')\n        end\n    end\n\n    for _, v in pairs(opts) do table.insert(args, v) end\n    request.multibulk(client, command, args)\nend\n\nlocal function zset_range_reply(reply, command, ...)\n    local args = {...}\n    local opts = args[4]\n    if opts and (opts.withscores or string.lower(tostring(opts)) == 'withscores') then\n        local new_reply = { }\n        for i = 1, #reply, 2 do\n            table.insert(new_reply, { reply[i], reply[i + 1] })\n        end\n        return new_reply\n    else\n        return reply\n    end\nend\n\nlocal function zset_store_request(client, command, ...)\n    local args, opts = {...}, { }\n\n    if #args >= 1 and type(args[#args]) == 'table' then\n        local options = table.remove(args, #args)\n        if options.weights and type(options.weights) == 'table' then\n            table.insert(opts, 'WEIGHTS')\n            for _, weight in ipairs(options.weights) do\n                table.insert(opts, weight)\n            end\n        end\n        if options.aggregate then\n            table.insert(opts, 'AGGREGATE')\n            table.insert(opts, options.aggregate)\n        end\n    end\n\n    for _, v in pairs(opts) do table.insert(args, v) end\n    request.multibulk(client, command, args)\nend\n\nlocal function mset_filter_args(client, command, ...)\n    local args, arguments = {...}, {}\n    if (#args == 1 and type(args[1]) == 'table') then\n        for k,v in pairs(args[1]) do\n            table.insert(arguments, k)\n            table.insert(arguments, v)\n        end\n    else\n        arguments = args\n    end\n    request.multibulk(client, command, arguments)\nend\n\nlocal function hash_multi_request_builder(builder_callback)\n    return function(client, command, ...)\n        local args, arguments = {...}, { }\n        if #args == 2 then\n            table.insert(arguments, args[1])\n            for k, v in pairs(args[2]) do\n                builder_callback(arguments, k, v)\n            end\n        else\n            arguments = args\n        end\n        request.multibulk(client, command, arguments)\n    end\nend\n\nlocal function parse_info(response)\n    local info = {}\n    response:gsub('([^\\r\\n]*)\\r\\n', function(kv)\n        local k,v = kv:match(('([^:]*):([^:]*)'):rep(1))\n        if (k:match('db%d+')) then\n            info[k] = {}\n            v:gsub(',', function(dbkv)\n                local dbk,dbv = kv:match('([^:]*)=([^:]*)')\n                info[k][dbk] = dbv\n            end)\n        else\n            info[k] = v\n        end\n    end)\n    return info\nend\n\nlocal function parse_info_new(response)\n    local info, current = {}, nil\n    response:gsub('([^\\r\\n]*)\\r\\n', function(kv)\n        if kv == '' then return end\n\n        local section = kv:match(('^# (%w+)'):rep(1))\n        if section then\n            current = section:lower()\n            info[current] = {}\n            return\n        end\n\n        local k,v = kv:match(('([^:]*):([^:]*)'):rep(1))\n        if (k:match('db%d+')) then\n            info[current][k] = {}\n            v:gsub(',', function(dbkv)\n                local dbk,dbv = kv:match('([^:]*)=([^:]*)')\n                info[current][dbk] = dbv\n            end)\n        else\n            info[current][k] = v\n        end\n    end)\n    return info\nend\n\nlocal function load_methods(proto, methods)\n    local redis = setmetatable ({}, getmetatable(proto))\n    for i, v in pairs(proto) do redis[i] = v end\n    for i, v in pairs(methods) do redis[i] = v end\n    return redis\nend\n\nlocal function create_client(proto, client_socket, methods)\n    local redis = load_methods(proto, methods)\n    redis.network = {\n        socket = client_socket,\n        read   = network.read,\n        write  = network.write,\n    }\n    redis.requests = {\n        multibulk = request.multibulk,\n    }\n    return redis\nend\n\n-- ############################################################################\n\nfunction network.write(client, buffer)\n    local _, err = client.network.socket:send(buffer)\n    if err then client.error(err) end\nend\n\nfunction network.read(client, len)\n    if len == nil then len = '*l' end\n    local line, err = client.network.socket:receive(len)\n    if not err then return line else client.error('connection error: ' .. err) end\nend\n\n-- ############################################################################\n\nfunction response.read(client)\n    local res = client.network.read(client)\n    local prefix  = res:sub(1, -#res)\n    local handler = protocol.prefixes[prefix]\n    if not handler then\n        client.error('unknown response prefix: '..prefix)\n    end\n    return handler(client, res)\nend\n\nfunction response.status(client, data)\n    local sub = data:sub(2)\n\n    if sub == protocol.ok then\n        return true\n    elseif sub == protocol.queued then\n        return { queued = true }\n    else\n        return sub\n    end\nend\n\nfunction response.error(client, data)\n    local err_line = data:sub(2)\n\n    if err_line:sub(1, 3) == protocol.err then\n        client.error('redis error: ' .. err_line:sub(5))\n    else\n        client.error('redis error: ' .. err_line)\n    end\nend\n\nfunction response.bulk(client, data)\n    local str = data:sub(2)\n    local len = tonumber(str)\n    if not len then\n        client.error('cannot parse ' .. str .. ' as data length')\n    end\n\n    if len == -1 then return nil end\n    local next_chunk = client.network.read(client, len + 2)\n    return next_chunk:sub(1, -3);\nend\n\nfunction response.multibulk(client, data)\n    local str = data:sub(2)\n    local list_count = tonumber(str)\n\n    if list_count == -1 then\n        return nil\n    else\n        local list = {}\n        if list_count > 0 then\n            for i = 1, list_count do\n                table.insert(list, i, response.read(client))\n            end\n        end\n        return list\n    end\nend\n\nfunction response.integer(client, data)\n    local res = data:sub(2)\n    local number = tonumber(res)\n\n    if not number then\n        if res == protocol.null then\n            return nil\n        end\n        client.error('cannot parse '..res..' as a numeric response.')\n    end\n\n    return number\nend\n\nprotocol.prefixes = {\n    ['+'] = response.status,\n    ['-'] = response.error,\n    ['$'] = response.bulk,\n    ['*'] = response.multibulk,\n    [':'] = response.integer,\n}\n\n-- ############################################################################\n\nfunction request.raw(client, buffer)\n    local bufferType = type(buffer)\n\n    if bufferType == 'table' then\n        client.network.write(client, table.concat(buffer))\n    elseif bufferType == 'string' then\n        client.network.write(client, buffer)\n    else\n        client.error('argument error: ' .. bufferType)\n    end\nend\n\nfunction request.multibulk(client, command, ...)\n    local args      = {...}\n    local args_len  = #args\n    local buffer    = { true, true }\n    local proto_nl  = protocol.newline\n\n    if args_len == 1 and type(args[1]) == 'table' then\n        args_len, args = #args[1], args[1]\n    end\n\n    buffer[1] = '*' .. tostring(args_len + 1) .. proto_nl\n    buffer[2] = '$' .. #command .. proto_nl .. command .. proto_nl\n\n    for _, argument in pairs(args) do\n        s_argument = tostring(argument)\n        table.insert(buffer, '$' .. #s_argument .. proto_nl .. s_argument .. proto_nl)\n    end\n\n    request.raw(client, buffer)\nend\n\n-- ############################################################################\n\nlocal function custom(command, send, parse)\n    return function(client, ...)\n        local has_reply = send(client, command, ...)\n        if has_reply == false then return end\n        local reply = response.read(client)\n\n        if type(reply) == 'table' and reply.queued then\n            reply.parser = parse\n            return reply\n        else\n            if parse then\n                return parse(reply, command, ...)\n            else\n                return reply\n            end\n        end\n    end\nend\n\nfunction command(command, opts)\n    if opts == nil or type(opts) == 'function' then\n        return custom(command, request.multibulk, opts)\n    else\n        return custom(command, opts.request or request.multibulk, opts.response)\n    end\nend\n\nlocal define_command_impl = function(target, name, opts)\n    local opts = opts or {}\n    target[string.lower(name)] = custom(\n        opts.command or string.upper(name),\n        opts.request or request.multibulk,\n        opts.response or nil\n    )\nend\n\nfunction define_command(name, opts)\n    define_command_impl(commands, name, opts)\nend\n\nlocal undefine_command_impl = function(target, name)\n    target[string.lower(name)] = nil\nend\n\nfunction undefine_command(name)\n    undefine_command_impl(commands, name)\nend\n\n-- ############################################################################\n\nlocal client_prototype = {}\n\nclient_prototype.raw_cmd = function(client, buffer)\n    request.raw(client, buffer .. protocol.newline)\n    return response.read(client)\nend\n\nclient_prototype.define_command = function(client, name, opts)\n    define_command_impl(client, name, opts)\nend\n\nclient_prototype.undefine_command = function(client, name)\n    undefine_command_impl(client, name)\nend\n\n-- Command pipelining\n\nclient_prototype.pipeline = function(client, block)\n    local requests, replies, parsers = {}, {}, {}\n    local socket_write, socket_read = client.network.write, client.network.read\n\n    client.network.write = function(_, buffer)\n        table.insert(requests, buffer)\n    end\n\n    -- TODO: this hack is necessary to temporarily reuse the current\n    --       request -> response handling implementation of redis-lua\n    --       without further changes in the code, but it will surely\n    --       disappear when the new command-definition infrastructure\n    --       will finally be in place.\n    client.network.read = function() return '+QUEUED' end\n\n    local pipeline = setmetatable({}, {\n        __index = function(env, name)\n            local cmd = client[name]\n            if not cmd then\n                client.error('unknown redis command: ' .. name, 2)\n            end\n            return function(self, ...)\n                local reply = cmd(client, ...)\n                table.insert(parsers, #requests, reply.parser)\n                return reply\n            end\n        end\n    })\n\n    local success, retval = pcall(block, pipeline)\n\n    client.network.write, client.network.read = socket_write, socket_read\n    if not success then client.error(retval, 0) end\n\n    client.network.write(client, table.concat(requests, ''))\n\n    for i = 1, #requests do\n        local reply, parser = response.read(client), parsers[i]\n        if parser then\n            reply = parser(reply)\n        end\n        table.insert(replies, i, reply)\n    end\n\n    return replies, #requests\nend\n\n-- Publish/Subscribe\n\ndo\n    local channels = function(channels)\n        if type(channels) == 'string' then\n            channels = { channels }\n        end\n        return channels\n    end\n\n    local subscribe = function(client, ...)\n        request.multibulk(client, 'subscribe', ...)\n    end\n    local psubscribe = function(client, ...)\n        request.multibulk(client, 'psubscribe', ...)\n    end\n    local unsubscribe = function(client, ...)\n        request.multibulk(client, 'unsubscribe')\n    end\n    local punsubscribe = function(client, ...)\n        request.multibulk(client, 'punsubscribe')\n    end\n\n    local consumer_loop = function(client)\n        local aborting, subscriptions = false, 0\n\n        local abort = function()\n            if not aborting then\n                unsubscribe(client)\n                punsubscribe(client)\n                aborting = true\n            end\n        end\n\n        return coroutine.wrap(function()\n            while true do\n                local message\n                local response = response.read(client)\n\n                if response[1] == 'pmessage' then\n                    message = {\n                        kind    = response[1],\n                        pattern = response[2],\n                        channel = response[3],\n                        payload = response[4],\n                    }\n                else\n                    message = {\n                        kind    = response[1],\n                        channel = response[2],\n                        payload = response[3],\n                    }\n                end\n\n                if string.match(message.kind, '^p?subscribe$') then\n                    subscriptions = subscriptions + 1\n                end\n                if string.match(message.kind, '^p?unsubscribe$') then\n                    subscriptions = subscriptions - 1\n                end\n\n                if aborting and subscriptions == 0 then\n                    break\n                end\n                coroutine.yield(message, abort)\n            end\n        end)\n    end\n\n    client_prototype.pubsub = function(client, subscriptions)\n        if type(subscriptions) == 'table' then\n            if subscriptions.subscribe then\n                subscribe(client, channels(subscriptions.subscribe))\n            end\n            if subscriptions.psubscribe then\n                psubscribe(client, channels(subscriptions.psubscribe))\n            end\n        end\n        return consumer_loop(client)\n    end\nend\n\n-- Redis transactions (MULTI/EXEC)\n\ndo\n    local function identity(...) return ... end\n    local emptytable = {}\n\n    local function initialize_transaction(client, options, block, queued_parsers)\n        local coro = coroutine.create(block)\n\n        if options.watch then\n            local watch_keys = {}\n            for _, key in pairs(options.watch) do\n                table.insert(watch_keys, key)\n            end\n            if #watch_keys > 0 then\n                client:watch(unpack(watch_keys))\n            end\n        end\n\n        local transaction_client = setmetatable({}, {__index=client})\n        transaction_client.exec  = function(...)\n            client.error('cannot use EXEC inside a transaction block')\n        end\n        transaction_client.multi = function(...)\n            coroutine.yield()\n        end\n        transaction_client.commands_queued = function()\n            return #queued_parsers\n        end\n\n        assert(coroutine.resume(coro, transaction_client))\n\n        transaction_client.multi = nil\n        transaction_client.discard = function(...)\n            local reply = client:discard()\n            for i, v in pairs(queued_parsers) do\n                queued_parsers[i]=nil\n            end\n            coro = initialize_transaction(client, options, block, queued_parsers)\n            return reply\n        end\n        transaction_client.watch = function(...)\n            client.error('WATCH inside MULTI is not allowed')\n        end\n        setmetatable(transaction_client, { __index = function(t, k)\n                local cmd = client[k]\n                if type(cmd) == \"function\" then\n                    local function queuey(self, ...)\n                        local reply = cmd(client, ...)\n                        assert((reply or emptytable).queued == true, 'a QUEUED reply was expected')\n                        table.insert(queued_parsers, reply.parser or identity)\n                        return reply\n                    end\n                    t[k]=queuey\n                    return queuey\n                else\n                    return cmd\n                end\n            end\n        })\n        client:multi()\n        return coro\n    end\n\n    local function transaction(client, options, coroutine_block, attempts)\n        local queued_parsers, replies = {}, {}\n        local retry = tonumber(attempts) or tonumber(options.retry) or 2\n        local coro = initialize_transaction(client, options, coroutine_block, queued_parsers)\n\n        local success, retval\n        if coroutine.status(coro) == 'suspended' then\n            success, retval = coroutine.resume(coro)\n        else\n            -- do not fail if the coroutine has not been resumed (missing t:multi() with CAS)\n            success, retval = true, 'empty transaction'\n        end\n        if #queued_parsers == 0 or not success then\n            client:discard()\n            assert(success, retval)\n            return replies, 0\n        end\n\n        local raw_replies = client:exec()\n        if not raw_replies then\n            if (retry or 0) <= 0 then\n                client.error(\"MULTI/EXEC transaction aborted by the server\")\n            else\n                --we're not quite done yet\n                return transaction(client, options, coroutine_block, retry - 1)\n            end\n        end\n\n        for i, parser in pairs(queued_parsers) do\n            table.insert(replies, i, parser(raw_replies[i]))\n        end\n\n        return replies, #queued_parsers\n    end\n\n    client_prototype.transaction = function(client, arg1, arg2)\n        local options, block\n        if not arg2 then\n            options, block = {}, arg1\n        elseif arg1 then --and arg2, implicitly\n            options, block = type(arg1)==\"table\" and arg1 or { arg1 }, arg2\n        else\n            client.error(\"Invalid parameters for redis transaction.\")\n        end\n\n        if not options.watch then\n            watch_keys = { }\n            for i, v in pairs(options) do\n                if tonumber(i) then\n                    table.insert(watch_keys, v)\n                    options[i] = nil\n                end\n            end\n            options.watch = watch_keys\n        elseif not (type(options.watch) == 'table') then\n            options.watch = { options.watch }\n        end\n\n        if not options.cas then\n            local tx_block = block\n            block = function(client, ...)\n                client:multi()\n                return tx_block(client, ...) --can't wrap this in pcall because we're in a coroutine.\n            end\n        end\n\n        return transaction(client, options, block)\n    end\nend\n\n-- ############################################################################\n\nlocal function connect_tcp(socket, parameters)\n    local host, port = parameters.host, tonumber(parameters.port)\n    local ok, err = socket:connect(host, port)\n    if not ok then\n        default_error_fn('could not connect to '..host..':'..port..' ['..err..']')\n    end\n    socket:setoption('tcp-nodelay', parameters.tcp_nodelay)\n    return socket\nend\n\nlocal function connect_unix(socket, parameters)\n    local ok, err = socket:connect(parameters.path)\n    if not ok then\n        default_error_fn('could not connect to '..parameters.path..' ['..err..']')\n    end\n    return socket\nend\n\nlocal function create_connection(parameters)\n    local perform_connection, socket\n\n    if parameters.scheme == 'unix' then\n        perform_connection, socket = connect_unix, require('socket.unix')\n        assert(socket, 'your build of LuaSocket does not support UNIX domain sockets')\n    else\n        if parameters.scheme then\n            local scheme = parameters.scheme\n            assert(scheme == 'redis' or scheme == 'tcp', 'invalid scheme: '..scheme)\n        end\n        perform_connection, socket = connect_tcp, require('socket').tcp\n    end\n\n    return perform_connection(socket(), parameters)\nend\n\nfunction connect(...)\n    local args, parameters = {...}, nil\n\n    if #args == 1 then\n        if type(args[1]) == 'table' then\n            parameters = args[1]\n        else\n            local uri = require('socket.url')\n            parameters = uri.parse(select(1, ...))\n            if parameters.scheme then\n                if parameters.query then\n                    for k, v in parameters.query:gmatch('([-_%w]+)=([-_%w]+)') do\n                        if k == 'tcp_nodelay' or k == 'tcp-nodelay' then\n                            parameters.tcp_nodelay = parse_boolean(v)\n                        end\n                    end\n                end\n            else\n                parameters.host = parameters.path\n            end\n        end\n    elseif #args > 1 then\n        local host, port = unpack(args)\n        parameters = { host = host, port = port }\n    end\n\n    local socket = create_connection(merge_defaults(parameters))\n    local client = create_client(client_prototype, socket, commands)\n    \n    client.error = default_error_fn\n\n    return client\nend\n\n-- ############################################################################\n\ncommands = {\n    -- commands operating on the key space\n    exists           = command('EXISTS', {\n        response = toboolean\n    }),\n    del              = command('DEL'),\n    type             = command('TYPE'),\n    rename           = command('RENAME'),\n    renamenx         = command('RENAMENX', {\n        response = toboolean\n    }),\n    expire           = command('EXPIRE', {\n        response = toboolean\n    }),\n    pexpire          = command('PEXPIRE', {     -- >= 2.6\n        response = toboolean\n    }),\n    expireat         = command('EXPIREAT', {\n        response = toboolean\n    }),\n    pexpireat        = command('PEXPIREAT', {   -- >= 2.6\n        response = toboolean\n    }),\n    ttl              = command('TTL'),\n    pttl             = command('PTTL'),         -- >= 2.6\n    move             = command('MOVE', {\n        response = toboolean\n    }),\n    dbsize           = command('DBSIZE'),\n    persist          = command('PERSIST', {     -- >= 2.2\n        response = toboolean\n    }),\n    keys             = command('KEYS', {\n        response = function(response)\n            if type(response) == 'string' then\n                -- backwards compatibility path for Redis < 2.0\n                local keys = {}\n                response:gsub('[^%s]+', function(key)\n                    table.insert(keys, key)\n                end)\n                response = keys\n            end\n            return response\n        end\n    }),\n    randomkey        = command('RANDOMKEY', {\n        response = function(response)\n            if response == '' then\n                return nil\n            else\n                return response\n            end\n        end\n    }),\n    sort             = command('SORT', {\n        request = function(client, command, key, params)\n            --[[ params = {\n                    by    = 'weight_*',\n                    get   = 'object_*',\n                    limit = { 0, 10 },\n                    sort  = 'desc',\n                    alpha = true,\n                } --]]\n            local query = { key }\n\n            if params then\n                if params.by then\n                    table.insert(query, 'BY')\n                    table.insert(query, params.by)\n                end\n\n                if type(params.limit) == 'table' then\n                    -- TODO: check for lower and upper limits\n                    table.insert(query, 'LIMIT')\n                    table.insert(query, params.limit[1])\n                    table.insert(query, params.limit[2])\n                end\n\n                if params.get then\n                    if (type(params.get) == 'table') then\n                        for _, getarg in pairs(params.get) do\n                            table.insert(query, 'GET')\n                            table.insert(query, getarg)\n                        end\n                    else\n                        table.insert(query, 'GET')\n                        table.insert(query, params.get)\n                    end\n                end\n\n                if params.sort then\n                    table.insert(query, params.sort)\n                end\n\n                if params.alpha == true then\n                    table.insert(query, 'ALPHA')\n                end\n\n                if params.store then\n                    table.insert(query, 'STORE')\n                    table.insert(query, params.store)\n                end\n            end\n\n            request.multibulk(client, command, query)\n        end\n    }),\n\n    -- commands operating on string values\n    set              = command('SET'),\n    setnx            = command('SETNX', {\n        response = toboolean\n    }),\n    setex            = command('SETEX'),        -- >= 2.0\n    psetex           = command('PSETEX'),       -- >= 2.6\n    mset             = command('MSET', {\n        request = mset_filter_args\n    }),\n    msetnx           = command('MSETNX', {\n        request  = mset_filter_args,\n        response = toboolean\n    }),\n    get              = command('GET'),\n    mget             = command('MGET'),\n    getset           = command('GETSET'),\n    incr             = command('INCR'),\n    incrby           = command('INCRBY'),\n    incrbyfloat      = command('INCRBYFLOAT', { -- >= 2.6\n        response = function(reply, command, ...)\n            return tonumber(reply)\n        end,\n    }),\n    decr             = command('DECR'),\n    decrby           = command('DECRBY'),\n    append           = command('APPEND'),       -- >= 2.0\n    substr           = command('SUBSTR'),       -- >= 2.0\n    strlen           = command('STRLEN'),       -- >= 2.2\n    setrange         = command('SETRANGE'),     -- >= 2.2\n    getrange         = command('GETRANGE'),     -- >= 2.2\n    setbit           = command('SETBIT'),       -- >= 2.2\n    getbit           = command('GETBIT'),       -- >= 2.2\n\n    -- commands operating on lists\n    rpush            = command('RPUSH'),\n    lpush            = command('LPUSH'),\n    llen             = command('LLEN'),\n    lrange           = command('LRANGE'),\n    ltrim            = command('LTRIM'),\n    lindex           = command('LINDEX'),\n    lset             = command('LSET'),\n    lrem             = command('LREM'),\n    lpop             = command('LPOP'),\n    rpop             = command('RPOP'),\n    rpoplpush        = command('RPOPLPUSH'),\n    blpop            = command('BLPOP'),        -- >= 2.0\n    brpop            = command('BRPOP'),        -- >= 2.0\n    rpushx           = command('RPUSHX'),       -- >= 2.2\n    lpushx           = command('LPUSHX'),       -- >= 2.2\n    linsert          = command('LINSERT'),      -- >= 2.2\n    brpoplpush       = command('BRPOPLPUSH'),   -- >= 2.2\n\n    -- commands operating on sets\n    sadd             = command('SADD', {\n        response = toboolean\n    }),\n    srem             = command('SREM', {\n        response = toboolean\n    }),\n    spop             = command('SPOP'),\n    smove            = command('SMOVE', {\n        response = toboolean\n    }),\n    scard            = command('SCARD'),\n    sismember        = command('SISMEMBER', {\n        response = toboolean\n    }),\n    sinter           = command('SINTER'),\n    sinterstore      = command('SINTERSTORE'),\n    sunion           = command('SUNION'),\n    sunionstore      = command('SUNIONSTORE'),\n    sdiff            = command('SDIFF'),\n    sdiffstore       = command('SDIFFSTORE'),\n    smembers         = command('SMEMBERS'),\n    srandmember      = command('SRANDMEMBER'),\n\n    -- commands operating on sorted sets\n    zadd             = command('ZADD', {\n        response = toboolean\n    }),\n    zincrby          = command('ZINCRBY'),\n    zrem             = command('ZREM', {\n        response = toboolean\n    }),\n    zrange           = command('ZRANGE', {\n        request  = zset_range_request,\n        response = zset_range_reply,\n    }),\n    zrevrange        = command('ZREVRANGE', {\n        request  = zset_range_request,\n        response = zset_range_reply,\n    }),\n    zrangebyscore    = command('ZRANGEBYSCORE', {\n        request  = zset_range_byscore_request,\n        response = zset_range_reply,\n    }),\n    zrevrangebyscore = command('ZREVRANGEBYSCORE', {    -- >= 2.2\n        request  = zset_range_byscore_request,\n        response = zset_range_reply,\n    }),\n    zunionstore      = command('ZUNIONSTORE', {         -- >= 2.0\n        request = zset_store_request\n    }),\n    zinterstore      = command('ZINTERSTORE', {         -- >= 2.0\n        request = zset_store_request\n    }),\n    zcount           = command('ZCOUNT'),\n    zcard            = command('ZCARD'),\n    zscore           = command('ZSCORE'),\n    zremrangebyscore = command('ZREMRANGEBYSCORE'),\n    zrank            = command('ZRANK'),                -- >= 2.0\n    zrevrank         = command('ZREVRANK'),             -- >= 2.0\n    zremrangebyrank  = command('ZREMRANGEBYRANK'),      -- >= 2.0\n\n    -- commands operating on hashes\n    hset             = command('HSET', {        -- >= 2.0\n        response = toboolean\n    }),\n    hsetnx           = command('HSETNX', {      -- >= 2.0\n        response = toboolean\n    }),\n    hmset            = command('HMSET', {       -- >= 2.0\n        request  = hash_multi_request_builder(function(args, k, v)\n            table.insert(args, k)\n            table.insert(args, v)\n        end),\n    }),\n    hincrby          = command('HINCRBY'),      -- >= 2.0\n    hincrbyfloat     = command('HINCRBYFLOAT', {-- >= 2.6\n        response = function(reply, command, ...)\n            return tonumber(reply)\n        end,\n    }),\n    hget             = command('HGET'),         -- >= 2.0\n    hmget            = command('HMGET', {       -- >= 2.0\n        request  = hash_multi_request_builder(function(args, k, v)\n            table.insert(args, v)\n        end),\n    }),\n    hdel             = command('HDEL', {        -- >= 2.0\n        response = toboolean\n    }),\n    hexists          = command('HEXISTS', {     -- >= 2.0\n        response = toboolean\n    }),\n    hlen             = command('HLEN'),         -- >= 2.0\n    hkeys            = command('HKEYS'),        -- >= 2.0\n    hvals            = command('HVALS'),        -- >= 2.0\n    hgetall          = command('HGETALL', {     -- >= 2.0\n        response = function(reply, command, ...)\n            local new_reply = { }\n            for i = 1, #reply, 2 do new_reply[reply[i]] = reply[i + 1] end\n            return new_reply\n        end\n    }),\n\n    -- connection related commands\n    ping             = command('PING', {\n        response = function(response) return response == 'PONG' end\n    }),\n    echo             = command('ECHO'),\n    auth             = command('AUTH'),\n    select           = command('SELECT'),\n    quit             = command('QUIT', {\n        request = fire_and_forget\n    }),\n\n    -- transactions\n    multi            = command('MULTI'),        -- >= 2.0\n    exec             = command('EXEC'),         -- >= 2.0\n    discard          = command('DISCARD'),      -- >= 2.0\n    watch            = command('WATCH'),        -- >= 2.2\n    unwatch          = command('UNWATCH'),      -- >= 2.2\n\n    -- publish - subscribe\n    subscribe        = command('SUBSCRIBE'),    -- >= 2.0\n    unsubscribe      = command('UNSUBSCRIBE'),  -- >= 2.0\n    psubscribe       = command('PSUBSCRIBE'),   -- >= 2.0\n    punsubscribe     = command('PUNSUBSCRIBE'), -- >= 2.0\n    publish          = command('PUBLISH'),      -- >= 2.0\n\n    -- redis scripting\n    eval             = command('EVAL'),         -- >= 2.6\n    evalsha          = command('EVALSHA'),      -- >= 2.6\n    script           = command('SCRIPT'),       -- >= 2.6\n\n    -- remote server control commands\n    bgrewriteaof     = command('BGREWRITEAOF'),\n    config           = command('CONFIG', {     -- >= 2.0\n        response = function(reply, command, ...)\n            if (type(reply) == 'table') then\n                local new_reply = { }\n                for i = 1, #reply, 2 do new_reply[reply[i]] = reply[i + 1] end\n                return new_reply\n            end\n\n            return reply\n        end\n    }),\n    client           = command('CLIENT'),       -- >= 2.4\n    slaveof          = command('SLAVEOF'),\n    save             = command('SAVE'),\n    bgsave           = command('BGSAVE'),\n    lastsave         = command('LASTSAVE'),\n    flushdb          = command('FLUSHDB'),\n    flushall         = command('FLUSHALL'),\n    shutdown         = command('SHUTDOWN', {\n        request = fire_and_forget\n    }),\n    slowlog          = command('SLOWLOG', {     -- >= 2.2.13\n        response = function(reply, command, ...)\n            if (type(reply) == 'table') then\n                local structured = { }\n                for index, entry in ipairs(reply) do\n                    structured[index] = {\n                        id = tonumber(entry[1]),\n                        timestamp = tonumber(entry[2]),\n                        duration = tonumber(entry[3]),\n                        command = entry[4],\n                    }\n                end\n                return structured\n            end\n\n            return reply\n        end\n    }),\n    info             = command('INFO', {\n        response = function(response)\n            if string.find(response, '^# ') then\n                return parse_info_new(response)\n            end\n            return parse_info(response)\n        end\n    }),\n}\n"
  },
  {
    "path": "t/lib/ljson.lua",
    "content": "local ngx_null = ngx.null\nlocal tostring = tostring\nlocal byte = string.byte\nlocal gsub = string.gsub\nlocal sort = table.sort\nlocal pairs = pairs\nlocal ipairs = ipairs\nlocal concat = table.concat\n\nlocal ok, new_tab = pcall(require, \"table.new\")\nif not ok then\n    new_tab = function (narr, nrec) return {} end\nend\n\nlocal _M = {}\n\nlocal metachars = {\n    ['\\t'] = '\\\\t',\n    [\"\\\\\"] = \"\\\\\\\\\",\n    ['\"'] = '\\\\\"',\n    ['\\r'] = '\\\\r',\n    ['\\n'] = '\\\\n',\n}\n\nlocal function encode_str(s)\n    -- XXX we will rewrite this when string.buffer is implemented\n    -- in LuaJIT 2.1 because string.gsub cannot be JIT compiled.\n    return gsub(s, '[\"\\\\\\r\\n\\t]', metachars)\nend\n\nlocal function is_arr(t)\n    local exp = 1\n    for k, _ in pairs(t) do\n        if k ~= exp then\n            return nil\n        end\n        exp = exp + 1\n    end\n    return exp - 1\nend\n\nlocal encode\n\nencode = function (v)\n    if v == nil or v == ngx_null then\n        return \"null\"\n    end\n\n    local typ = type(v)\n    if typ == 'string' then\n        return '\"' .. encode_str(v) .. '\"'\n    end\n\n    if typ == 'number' or typ == 'boolean' then\n        return tostring(v)\n    end\n\n    if typ == 'table' then\n        local n = is_arr(v)\n        if n then\n            local bits = new_tab(n, 0)\n            for i, elem in ipairs(v) do\n                bits[i] = encode(elem)\n            end\n            return \"[\" .. concat(bits, \",\") .. \"]\"\n        end\n\n        local keys = {}\n        local i = 0\n        for key, _ in pairs(v) do\n            i = i + 1\n            keys[i] = key\n        end\n        sort(keys)\n\n        local bits = new_tab(0, i)\n        i = 0\n        for _, key in ipairs(keys) do\n            i = i + 1\n            bits[i] = encode(key) .. \":\" .. encode(v[key])\n        end\n        return \"{\" .. concat(bits, \",\") .. \"}\"\n    end\n\n    return '\"<' .. typ .. '>\"'\nend\n_M.encode = encode\n\nreturn _M\n"
  },
  {
    "path": "tapset/ngx_lua.stp",
    "content": "function ngx_http_lua_ctx_context(r)\n{\n\n}\n\n"
  },
  {
    "path": "util/build-with-dd.sh",
    "content": "#!/usr/bin/env bash\n\n# this script is for developers only.\n# dependent on the ngx-build script from the nginx-devel-utils repository:\n#   https://github.com/openresty/nginx-devel-utils/blob/master/ngx-build\n# the resulting nginx is located at ./work/nginx/sbin/nginx\n\nroot=`pwd`\nversion=${1:-1.4.1}\nhome=~\nforce=$2\n\nadd_fake_shm_module=\"--add-module=$root/t/data/fake-shm-module\"\n\ntime ngx-build $force $version \\\n            --with-threads \\\n            --with-pcre-jit \\\n            --with-ipv6 \\\n            --with-cc-opt=\"-DNGX_LUA_USE_ASSERT -I$PCRE2_INC -I$OPENSSL_INC -DDDEBUG=1\" \\\n            --with-http_v2_module \\\n            --with-http_v3_module \\\n            --with-http_realip_module \\\n            --with-http_ssl_module \\\n            --add-module=$root/../ndk-nginx-module \\\n            --add-module=$root/../set-misc-nginx-module \\\n            --with-ld-opt=\"-L$PCRE2_LIB -L$OPENSSL_LIB -Wl,-rpath,$PCRE2_LIB:$LIBDRIZZLE_LIB:$OPENSSL_LIB\" \\\n            --without-mail_pop3_module \\\n            --without-mail_imap_module \\\n            --with-http_image_filter_module \\\n            --without-mail_smtp_module \\\n            --with-stream \\\n            --with-stream_ssl_module \\\n            --without-http_upstream_ip_hash_module \\\n            --without-http_memcached_module \\\n            --without-http_auth_basic_module \\\n            --without-http_userid_module \\\n            --with-http_auth_request_module \\\n            --add-module=$root/../echo-nginx-module \\\n            --add-module=$root/../memc-nginx-module \\\n            --add-module=$root/../srcache-nginx-module \\\n            --add-module=$root \\\n            --add-module=$root/../lua-upstream-nginx-module \\\n            --add-module=$root/../headers-more-nginx-module \\\n            --add-module=$root/../drizzle-nginx-module \\\n            --add-module=$root/../rds-json-nginx-module \\\n            --add-module=$root/../coolkit-nginx-module \\\n            --add-module=$root/../redis2-nginx-module \\\n            --add-module=$root/../stream-lua-nginx-module \\\n            --add-module=$root/t/data/fake-module \\\n            $add_fake_shm_module \\\n            --add-module=$root/t/data/fake-delayed-load-module \\\n            --with-http_gunzip_module \\\n            --with-http_dav_module \\\n            --with-select_module \\\n            --with-poll_module \\\n            $opts \\\n            --with-debug\n\n"
  },
  {
    "path": "util/build-without-ssl.sh",
    "content": "#!/usr/bin/env bash\n\n# this script is for developers only.\n# dependent on the ngx-build script from the nginx-devel-utils repository:\n#   https://github.com/openresty/nginx-devel-utils/blob/master/ngx-build\n# the resulting nginx is located at ./work/nginx/sbin/nginx\n\nroot=`pwd`\nversion=${1:-1.4.1}\nhome=~\nforce=$2\n\nadd_fake_shm_module=\"--add-module=$root/t/data/fake-shm-module\"\n\nrm -fr buildroot\ntime ngx-build $force $version \\\n            --with-threads \\\n            --with-pcre-jit \\\n            --with-ipv6 \\\n            --with-cc-opt=\"-DNGX_LUA_USE_ASSERT -I$PCRE2_INC\" \\\n            --with-http_v2_module \\\n            --with-http_realip_module \\\n            --add-module=$root/../ndk-nginx-module \\\n            --add-module=$root/../set-misc-nginx-module \\\n            --with-ld-opt=\"-L$PCRE2_LIB -Wl,-rpath,$PCRE2_LIB:$LIBDRIZZLE_LIB\" \\\n            --without-mail_pop3_module \\\n            --without-mail_imap_module \\\n            --with-http_image_filter_module \\\n            --without-mail_smtp_module \\\n            --with-stream \\\n            --without-http_upstream_ip_hash_module \\\n            --without-http_memcached_module \\\n            --without-http_auth_basic_module \\\n            --without-http_userid_module \\\n            --with-http_auth_request_module \\\n                --add-module=$root/../echo-nginx-module \\\n                --add-module=$root/../memc-nginx-module \\\n                --add-module=$root/../srcache-nginx-module \\\n                --add-module=$root \\\n                --add-module=$root/../lua-upstream-nginx-module \\\n              --add-module=$root/../headers-more-nginx-module \\\n                --add-module=$root/../drizzle-nginx-module \\\n                --add-module=$root/../rds-json-nginx-module \\\n                --add-module=$root/../coolkit-nginx-module \\\n                --add-module=$root/../redis2-nginx-module \\\n                --add-module=$root/../stream-lua-nginx-module \\\n                --add-module=$root/t/data/fake-module \\\n                $add_fake_shm_module \\\n                --add-module=$root/t/data/fake-delayed-load-module \\\n                --with-http_gunzip_module \\\n                --with-http_dav_module \\\n          --with-select_module \\\n          --with-poll_module \\\n                $opts \\\n                --with-debug\n\n"
  },
  {
    "path": "util/build.sh",
    "content": "#!/usr/bin/env bash\n\n# this script is for developers only.\n# dependent on the ngx-build script from the nginx-devel-utils repository:\n#   https://github.com/openresty/nginx-devel-utils/blob/master/ngx-build\n# the resulting nginx is located at ./work/nginx/sbin/nginx\n\nroot=`pwd`\nversion=${1:-1.4.1}\nhome=~\nforce=$2\n\n# the ngx-build script is from https://github.com/agentzh/nginx-devel-utils\n\n            #--add-module=$home/work/nginx_upload_module-2.2.0 \\\n\n            #--without-pcre \\\n            #--without-http_rewrite_module \\\n            #--without-http_autoindex_module \\\n            #--with-cc=gcc46 \\\n            #--with-cc=clang \\\n            #--without-http_referer_module \\\n            #--with-http_spdy_module \\\n\nadd_fake_shm_module=\"--add-module=$root/t/data/fake-shm-module\"\n\nanswer=`$root/util/ver-ge \"$version\" 1.25.1`\n\ntime ngx-build $force $version \\\n          --with-threads \\\n          --with-pcre-jit \\\n          --with-ipv6 \\\n          --with-cc-opt=\"-DNGX_LUA_USE_ASSERT -I$PCRE2_INC -I$OPENSSL_INC\" \\\n          --with-http_v2_module \\\n          --with-http_v3_module \\\n          --with-http_realip_module \\\n          --with-http_ssl_module \\\n          --add-module=$root/../ndk-nginx-module \\\n          --add-module=$root/../set-misc-nginx-module \\\n          --with-ld-opt=\"-L$PCRE2_LIB -L$OPENSSL_LIB -L$LUAJIT_LIB -Wl,-rpath,$PCRE2_LIB:$LIBDRIZZLE_LIB:$OPENSSL_LIB:$LUAJIT_LIB\" \\\n          --without-mail_pop3_module \\\n          --without-mail_imap_module \\\n          --with-http_image_filter_module \\\n          --without-mail_smtp_module \\\n          --with-stream \\\n          --with-stream_ssl_module \\\n          --without-http_upstream_ip_hash_module \\\n          --without-http_memcached_module \\\n          --without-http_auth_basic_module \\\n          --without-http_userid_module \\\n          --with-http_auth_request_module \\\n          --add-module=$root/../echo-nginx-module \\\n          --add-module=$root/../memc-nginx-module \\\n          --add-module=$root/../srcache-nginx-module \\\n          --add-module=$root \\\n          --add-module=$root/../lua-upstream-nginx-module \\\n          --add-module=$root/../headers-more-nginx-module \\\n          --add-module=$root/../drizzle-nginx-module \\\n          --add-module=$root/../rds-json-nginx-module \\\n          --add-module=$root/../coolkit-nginx-module \\\n          --add-module=$root/../redis2-nginx-module \\\n          --add-module=$root/../stream-lua-nginx-module \\\n          --add-module=$root/t/data/fake-module \\\n          $add_fake_shm_module \\\n          --add-module=$root/t/data/fake-delayed-load-module \\\n          --with-http_gunzip_module \\\n          --with-http_dav_module \\\n          --with-select_module \\\n          --with-poll_module \\\n          $opts \\\n          --with-debug\n\n"
  },
  {
    "path": "util/fix-comments",
    "content": "#!/usr/bin/env perl\n\nuse strict;\nuse warnings;\n\nfor my $infile (@ARGV) {\n    warn \"Processing $infile...\\n\";\n    open my $in, $infile or\n        die \"Cannot open $infile for reading: $!\\n\";\n    my $s;\n    my $changed = 0;\n    while (<$in>) {\n        $changed += s{//(.*)}{/* $1 */};\n        $s .= $_;\n    }\n    close $in;\n\n    if ($changed) {\n        my $outfile = $infile;\n        open my $out, \">$outfile\" or\n            die \"Cannot open $outfile for writing: $!\\n\";\n        print $out $s;\n        close $out;\n        warn \"Wrote $outfile\\n\";\n    }\n}\n\n"
  },
  {
    "path": "util/gen-lexer-c",
    "content": "#!/usr/bin/env bash\n\nif [ -z \"$1\" ]; then\n    level=0\nelse\n    level=\"$1\"\nfi\n\n#echo '{' '}' '\\[=*\\[' '--\\[=*\\[' '\\]=*\\]' '--[^\\n]*' '\"(?:\\\\[^\\n]|[^\"\\n\\\\])*\"' $'\\'(?:\\\\\\\\[^\\\\n]|[^\\'\\\\n\\\\\\\\])*\\''\n\n# we need the re.pl script here:\n#   https://github.com/openresty/sregex/blob/dfa-multi-re/re.pl\nre.pl -W --no-main -c --cc=\"clang -O2\" \\\n    --func-name ngx_http_lua_lex \\\n    --header ngx_http_lua_lex.h -o src/ngx_http_lua_lex.c \\\n    --debug=$level -n 8 \\\n    -- '{' '}' '\\[=*\\[' '--\\[=*\\[' '\\]=*\\]' '--[^\\n]*' '\"(?:\\\\[^\\n]|[^\"\\n\\\\])*\"' $'\\'(?:\\\\\\\\[^\\\\n]|[^\\'\\\\n\\\\\\\\])*\\'' \\\n    || exit 1\n"
  },
  {
    "path": "util/nc_server.py",
    "content": "import select, socket\nserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\nserver.setblocking(0)\nserver.bind(('localhost', 65110))\nserver.listen(5)\ninputs = [server]\n\nwhile inputs:\n    outputs = []\n    readable, writable, exceptional = select.select(\n        inputs, outputs, inputs)\n    for s in readable:\n        if s is server:\n            connection, client_address = s.accept()\n            connection.setblocking(0)\n            inputs.append(connection)\n        else:\n            data = s.recv(1024)\n            if not data:\n                inputs.remove(s)\n                s.close()\n\n    for s in exceptional:\n        inputs.remove(s)\n        s.close()\n"
  },
  {
    "path": "util/ngx-links",
    "content": "#!/usr/bin/env perl\n\nuse strict;\nuse warnings;\n\nuse Cwd qw( cwd );\nuse Getopt::Std;\n\nmy %opts;\ngetopts('f', \\%opts) or\n    die \"Usage: $0 [-f]\nOptions:\n    -f          Override existing symbolic links with force\n\";\n\nmy $root = shift || 'src';\n\nmy $force = $opts{f};\n\nopendir my $dir, $root\n    or die \"Can't open directory src/ for reading: $!\\n\";\n\nmy @links;\n\nwhile (my $entry = readdir $dir) {\n    my ($base, $ext);\n\n    my $source = \"$root/$entry\";\n\n    if (-l $source || -d $source) {\n        warn \"skipping $source\\n\";\n        next;\n    }\n\n    if ($entry =~ m{ ^ ngx_ (?: \\w+ _ )+ (\\w+) \\. ([ch]|rl) $}x) {\n        ($base, $ext) = ($1, $2);\n    } else {\n        next;\n    }\n\n    my $target = \"$root/$base.$ext\";\n    if (-e $target && ! -l $target) {\n        die \"target $target already exists, and not a symlink, not overriding...Abort.\\n\";\n    } elsif (-l $target) {\n        #warn \"it's a link\";\n        if ( ! $force ) {\n            die \"target $target already exists, not overriding...Abort.\\n\";\n        }\n        warn \"overriding existing symlink $target\\n\";\n    }\n    #warn \"creating $target --> $root/$entry\\n\";\n    system(\"ln -svf `pwd`/$source $target\") == 0 or\n        die \"Failed to create the symlink\\n\";;\n\n    push @links, $target;\n}\n\nprint join(\"\\n\", @links), \"\\n\";\n\nclose $dir;\n\n\n"
  },
  {
    "path": "util/retab",
    "content": "#!/bin/bash\n\nfor f in $*; do\n\tif [ -f \"$f\" ]; then\n\t\tvim -c retab -c x $f\n\tfi\ndone\n\n"
  },
  {
    "path": "util/revim",
    "content": "#!/usr/bin/perl\n# vim:set ft=perl ts=4 sw=4 et fdm=marker:\n#\n# revim - add customized vim modeline for given files\n# Copyright (c) 2010 chaoslawful\n\nuse strict;\nuse warnings;\nuse Getopt::Std;\n\nmy %opts;\n\ngetopts('hm:', \\%opts);\n\nif($opts{h} or !@ARGV) {\n    die <<EOT;\nUsage: revim [-m <vim modeline>] src/*\n\nIn <vim modeline>, the following placeholder(s) can be used:\n\n    * %t - Expands to vim file type. E.g. 'c' for .c files, 'perl' for .pl files.\nEOT\n}\n\nmy $vim_ml = $opts{m} || \"vim:set ft=%t ts=4 sw=4 et fdm=marker:\";\nmy @files = map glob, @ARGV;\nfor my $file (@files) {\n    next if -d $file;\n    my ($ft, $ml) = detect_filetype($file, $vim_ml);\n    next if !defined($ft);\n    revim($file, $ml);\n}\n\nsub detect_filetype\n{\n    my ($f, $tmpl) = @_;\n    my ($ft, $lcmt, $rcmt);\n    my %phs;\n\n    if($f =~ /.([cC]|[hH])$/) {\n        $ft = \"c\";\n        ($lcmt, $rcmt) = (\"/* \", \" */\");\n    } elsif($f =~ /.(pl|pm)$/) {\n        $ft = \"perl\";\n        ($lcmt, $rcmt) = (\"# \", \"\");\n    } elsif($f =~ /.t_?$/) {\n        # assuming tests are written in perl\n        $ft = \"perl\";\n        ($lcmt, $rcmt) = (\"# \", \"\");\n    } else {\n        $ft = undef;\n    }\n\n    if(defined($ft)) {\n        %phs = (\n            \"%t\" => $ft,\n        );\n\n        $tmpl =~ s/(%[a-z])/$phs{$1}/ge;\n        $tmpl =~ s/^/$lcmt/;\n        $tmpl =~ s/$/$rcmt/;\n\n        return ($ft, $tmpl);\n    }\n\n    return (undef, undef);\n}\n\nsub revim\n{\n    my ($f, $ml) = @_;\n    my @lines;\n\n    open my $in, $f\n        or die \"Can't open $f for reading: $!\";\n    while(<$in>) {\n        push(@lines, $_);\n    }\n    close $in;\n\n    my @nlines = grep {!/\\bvim?:/} @lines;\n    warn \"revim: $f:\\tremoved existing vim modeline.\\n\"\n        if(@nlines != @lines);\n\n    if($nlines[0] =~ /^#!/) {    # has shebang line\n        my $shebang = shift @nlines;\n        unshift(@nlines, $shebang, \"$ml\\n\");\n    } else {\n        unshift(@nlines, \"$ml\\n\");\n    }\n\n    my $text = join '', @nlines;\n\n    open my $out, \"> $f\"\n        or die \"Can't open $f for writing: $!\";\n    binmode $out;\n    print $out $text;\n    close $out;\n\n    warn \"revim: $f:\\tdone.\\n\";\n}\n\n"
  },
  {
    "path": "util/run-ci.sh",
    "content": "#!/bin/bash\n\n#export CC=clang\nexport CC=gcc\nexport NGX_BUILD_CC=$CC\n\nmkdir -p download-cache\n\nexport JOBS=$(nproc)\nexport NGX_BUILD_JOBS=$JOBS\nexport VALGRIND_INC=/usr/include/valgrind/\n\nexport LUAJIT_PREFIX=/opt/luajit21\nexport LUAJIT_LIB=$LUAJIT_PREFIX/lib\nexport LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1\nexport LUA_INCLUDE_DIR=$LUAJIT_INC\n\nexport PCRE2_VER=10.45\nexport PCRE2_PREFIX=/opt/pcre2\nexport PCRE2_LIB=$PCRE2_PREFIX/lib\nexport PCRE2_INC=$PCRE2_PREFIX/include\n\nexport OPENSSL_VER=3.5.0\nexport OPENSSL_PATCH_VER=3.5.0\nexport OPENSSL_PREFIX=/opt/ssl3\nexport OPENSSL_LIB=$OPENSSL_PREFIX/lib\nexport OPENSSL_INC=$OPENSSL_PREFIX/include\n\nexport LIBDRIZZLE_PREFIX=/opt/drizzle\nexport LIBDRIZZLE_INC=$LIBDRIZZLE_PREFIX/include/libdrizzle-1.0\nexport LIBDRIZZLE_LIB=$LIBDRIZZLE_PREFIX/lib\nexport DRIZZLE_VER=2011.07.21\n\n#export TEST_NGINX_SLEEP=0.006\nexport NGINX_VERSION=1.27.1\n#export NGX_BUILD_ASAN=1\n\nexport PATH=/opt/bin:$PWD/work/nginx/sbin:$PWD/openresty-devel-utils:$PATH\n\nif [ ! -f /opt/bin/curl ]; then\n    wget https://github.com/stunnel/static-curl/releases/download/8.14.1/curl-linux-x86_64-glibc-8.14.1.tar.xz\n    tar -xf curl-linux-x86_64-glibc-8.14.1.tar.xz\n    tar -xf curl-linux-x86_64-glibc-8.14.1.tar.xz\n    sudo mkdir -p /opt/bin\n    sudo mv curl /opt/bin/\nfi\n\nfunction git_download()\n{\n    dir=${!#}\n    if [ ! -d $dir ]; then\n        git clone $@\n    fi\n}\n\nfunction download_deps()\n{\n    if [ ! -f download-cache/drizzle7-$DRIZZLE_VER.tar.gz ]; then\n        wget -P download-cache https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/drizzle7-$DRIZZLE_VER.tar.gz\n    fi\n\n    if [ ! -f download-cache/pcre2-$PCRE2_VER.tar.gz ]; then\n       wget -P download-cache https://github.com/PCRE2Project/pcre2/releases/download/pcre2-${PCRE2_VER}/pcre2-${PCRE2_VER}.tar.gz\n    fi\n\n    if [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then\n       wget -P download-cache https://github.com/openssl/openssl/releases/download/openssl-$OPENSSL_VER/openssl-$OPENSSL_VER.tar.gz\n    fi\n\n    git_download https://github.com/openresty/test-nginx.git\n    git_download https://github.com/openresty/openresty.git ../openresty\n    git_download https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx\n    git_download https://github.com/openresty/openresty-devel-utils.git\n    git_download https://github.com/openresty/mockeagain.git\n    git_download https://github.com/openresty/lua-cjson.git lua-cjson\n    git_download https://github.com/openresty/lua-upstream-nginx-module.git ../lua-upstream-nginx-module\n    git_download https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module\n    git_download https://github.com/openresty/nginx-eval-module.git ../nginx-eval-module\n    git_download https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module\n    git_download https://github.com/FRiCKLE/ngx_coolkit.git ../coolkit-nginx-module\n    git_download https://github.com/openresty/headers-more-nginx-module.git ../headers-more-nginx-module\n    git_download https://github.com/openresty/drizzle-nginx-module.git ../drizzle-nginx-module\n    git_download https://github.com/openresty/set-misc-nginx-module.git ../set-misc-nginx-module\n    git_download https://github.com/openresty/memc-nginx-module.git ../memc-nginx-module\n    git_download https://github.com/openresty/rds-json-nginx-module.git ../rds-json-nginx-module\n    git_download https://github.com/openresty/srcache-nginx-module.git ../srcache-nginx-module\n    git_download https://github.com/openresty/redis2-nginx-module.git ../redis2-nginx-module\n    git_download https://github.com/openresty/lua-resty-core.git ../lua-resty-core\n    git_download https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache\n    git_download https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql\n    git_download https://github.com/openresty/lua-resty-string.git ../lua-resty-string\n    git_download https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module\n    git_download -b v2.1-agentzh https://github.com/openresty/luajit2.git luajit2\n}\n\nfunction make_deps()\n{\n    if [ \"$TEST_NGINX_BUILD_DEPS\" = \"n\" ]; then\n        return\n    fi\n\n    cd luajit2/\n    make clean\n    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)\n    sudo make install PREFIX=$LUAJIT_PREFIX\n    cd ..\n \n    tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz\n    cd openssl-$OPENSSL_VER/\n    patch -p1 < ../../openresty/patches/openssl-$OPENSSL_PATCH_VER-sess_set_get_cb_yield.patch > ssl.log\n    ./config -DOPENSSL_TLS_SECURITY_LEVEL=1 shared enable-ssl3 enable-ssl3-method -g -O2 --prefix=$OPENSSL_PREFIX --libdir=lib -DPURIFY >> ssl.log\n    make -j$JOBS >> ssl.log\n    sudo make PATH=$PATH install_sw >> ssl.log\n    cd ..\n \n    tar zxf download-cache/pcre2-$PCRE2_VER.tar.gz;\n    cd pcre2-$PCRE2_VER/;\n    ./configure --prefix=$PCRE2_PREFIX --enable-jit --enable-utf\n    sudo PATH=$PATH make install\n    cd ..\n \n    tar xzf download-cache/drizzle7-$DRIZZLE_VER.tar.gz && cd drizzle7-$DRIZZLE_VER\n    ./configure --prefix=$LIBDRIZZLE_PREFIX --without-server\n    make libdrizzle-1.0 -j$JOBS\n    sudo make install-libdrizzle-1.0\n    cd ..\n\n    cd mockeagain/ && make CC=$CC -j$JOBS\n    cd ..\n\n    cd lua-cjson/ && make -j$JOBS && sudo make install\n    cd ..\n}\n\nfunction make_ngx()\n{\n    if [ \"$TEST_NGINX_FRESH_BUILD\" = \"y\" ]; then\n        rm -fr buildroot\n    fi\n\n    sh util/build.sh $NGINX_VERSION 2>&1 | tee build.log\n}\n\ndownload_deps\nmake_deps\nmake_ngx\n\nfind t -name \"*.t\" | xargs reindex >/dev/null 2>&1\n\n#nginx -V\n\nexport LD_PRELOAD=$PWD/mockeagain/mockeagain.so\nexport LD_LIBRARY_PATH=$PWD/mockeagain:$LD_LIBRARY_PATH\n\n#export ASAN_OPTIONS=detect_leaks=0,log_path=/tmp/asan/asan,log_exe_name=true\n#export LD_PRELOAD=/lib64/libasan.so.5:$PWD/mockeagain/mockeagain.so\n\nexport LD_LIBRARY_PATH=$LUAJIT_LIB:$OPENSSL_LIB:$LD_LIBRARY_PATH\nexport TEST_NGINX_RESOLVER=8.8.4.4\n\n#export TEST_NGINX_NO_CLEAN=1\n#export TEST_NGINX_CHECK_LEAK=1\n#export TEST_NGINX_CHECK_LEAK_COUNT=100\n#export TEST_NGINX_TIMEOUT=5\n\n#export TEST_NGINX_USE_VALGRIND=1\n#export TEST_NGINX_VALGRIND_EXIT_ON_FIRST_ERR=1\n#export TEST_NGINX_USE_HTTP2=1\n\nexport MALLOC_PERTURB_=33\nexport TEST_NGINX_HTTP3_CRT=$PWD/t/cert/http3/http3.crt\nexport TEST_NGINX_HTTP3_KEY=$PWD/t/cert/http3/http3.key\n#export TEST_NGINX_USE_HTTP3=1\n\n#export TEST_NGINX_VERBOSE=1\n\n#export TEST_NGINX_EVENT_TYPE=poll\n#export TEST_NGINX_POSTPONE_OUTPUT=1\n#export MOCKEAGAIN=r\n#export MOCKEAGAIN=w\n#export MOCKEAGAIN=rw\n#export MOCKEAGAIN_VERBOSE=1\n\nldd `which nginx`|grep -E 'luajit|ssl|pcre'\nwhich nginx\nnginx -V\n\n#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)\"\n\n\n# comment out TEST_NGINX_RANDOMIZE when debugging one test case\nexport TEST_NGINX_RANDOMIZE=1\nprove -j$JOBS -I. -Itest-nginx/inc -Itest-nginx/lib -r t/\n"
  },
  {
    "path": "util/run_test.sh",
    "content": "#!/bin/bash\nscript_dir=$(dirname $0)\nroot=$(readlink -f $script_dir/..)\ntestfile=${1:-$root/t/*.t $root/t/*/*.t}\ncd $root\n$script_dir/reindex $testfile\nexport PATH=$root/work/sbin:$PATH\nkillall nginx\nprove -I$root/../test-nginx/lib $testfile\n\n"
  },
  {
    "path": "util/update-readme.sh",
    "content": "#!/bin/bash\n\nperl util/wiki2pod.pl doc/manpage.wiki > /tmp/a.pod && pod2text /tmp/a.pod > README\n\n"
  },
  {
    "path": "util/ver-ge",
    "content": "#!/usr/bin/env perl\n\nuse strict;\nuse warnings;\n\nsub usage {\n    die \"Usage: $0 <ver1> <ver2>\\n\";\n}\n\nmy $a = shift or usage();\nmy $b = shift or usage();\n\nmy @as = split /\\./, $a;\nmy @bs = split /\\./, $b;\n\nmy $n = @as > @bs ? scalar(@as) : scalar(@bs);\n\nfor (my $i = 0; $i < $n; $i++) {\n    my $x = $as[$i];\n    my $y = $bs[$i];\n\n    if (!defined $x) {\n        $x = 0;\n    }\n\n    if (!defined $y) {\n        $y = 0;\n    }\n\n    if ($x > $y) {\n        print \"Y\\n\";\n        exit;\n\n    } elsif ($x < $y) {\n        print \"N\\n\";\n        exit;\n    }\n}\n\nprint \"Y\\n\";\n\n"
  },
  {
    "path": "valgrind.suppress",
    "content": "{\n   <insert_a_suppression_name_here>\n   Memcheck:Addr1\n   fun:ngx_init_cycle\n   fun:ngx_master_process_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Addr4\n   fun:ngx_init_cycle\n   fun:ngx_master_process_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Cond\n   fun:ngx_vslprintf\n   fun:ngx_snprintf\n   fun:ngx_sock_ntop\n   fun:ngx_event_accept\n   fun:ngx_epoll_process_events\n   fun:ngx_process_events_and_timers\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Addr1\n   fun:ngx_vslprintf\n   fun:ngx_snprintf\n   fun:ngx_sock_ntop\n   fun:ngx_event_accept\n}\n{\n   <insert_a_suppression_name_here>\n   exp-sgcheck:SorG\n   fun:ngx_http_lua_ndk_set_var_get\n}\n{\n   <insert_a_suppression_name_here>\n   exp-sgcheck:SorG\n   fun:ngx_http_variables_init_vars\n   fun:ngx_http_block\n}\n{\n   <insert_a_suppression_name_here>\n   exp-sgcheck:SorG\n   fun:ngx_conf_parse\n}\n{\n   <insert_a_suppression_name_here>\n   exp-sgcheck:SorG\n   fun:ngx_vslprintf\n   fun:ngx_log_error_core\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   epoll_ctl(event)\n   fun:epoll_ctl\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Cond\n   fun:ngx_conf_flush_files\n   fun:ngx_single_process_cycle\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Cond\n   fun:memcpy\n   fun:ngx_vslprintf\n   fun:ngx_log_error_core\n   fun:ngx_http_charset_header_filter\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   socketcall.setsockopt(optval)\n   fun:setsockopt\n   fun:drizzle_state_connect\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Cond\n   fun:ngx_conf_flush_files\n   fun:ngx_single_process_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_event_process_init\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   sendmsg(mmsg[0].msg_hdr)\n   fun:sendmmsg\n   fun:__libc_res_nsend\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   sendmsg(msg.msg_iov[0])\n   fun:__sendmsg_nocancel\n   fun:ngx_write_channel\n   fun:ngx_pass_open_channel\n   fun:ngx_start_cache_manager_processes\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Cond\n   fun:ngx_init_cycle\n   fun:ngx_master_process_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Cond\n   fun:index\n   fun:expand_dynamic_string_token\n   fun:_dl_map_object\n   fun:map_doit\n   fun:_dl_catch_error\n   fun:do_preload\n   fun:dl_main\n   fun:_dl_sysdep_start\n   fun:_dl_start\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   sendmsg(mmsg[0].msg_hdr)\n   fun:sendmmsg\n   fun:__libc_res_nsend\n   fun:__libc_res_nquery\n   fun:__libc_res_nquerydomain\n   fun:__libc_res_nsearch\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   match-leak-kinds: definite\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_set_environment\n   fun:ngx_single_process_cycle\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Cond\n   obj:*\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   match-leak-kinds: definite\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_set_environment\n   fun:ngx_worker_process_init\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   match-leak-kinds: definite\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_create_pool\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   epoll_pwait(sigmask)\n   fun:epoll_pwait\n   fun:epoll_wait\n   fun:ngx_epoll_process_events\n   fun:ngx_process_events_and_timers\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   epoll_pwait(sigmask)\n   fun:epoll_pwait\n   fun:epoll_wait\n   fun:ngx_epoll_test_rdhup\n   fun:ngx_epoll_init\n   fun:ngx_event_process_init\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   epoll_pwait(sigmask)\n   fun:epoll_pwait\n   fun:ngx_epoll_process_events\n   fun:ngx_process_events_and_timers\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   epoll_pwait(sigmask)\n   fun:epoll_pwait\n   fun:ngx_epoll_test_rdhup\n   fun:ngx_epoll_init\n   fun:ngx_event_process_init\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   sendmsg(msg.msg_iov[0])\n   fun:__sendmsg_nocancel\n   fun:ngx_write_channel\n   fun:ngx_pass_open_channel\n   fun:ngx_start_worker_processes\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   sendmsg(msg.msg_iov[0])\n   fun:__sendmsg_nocancel\n   fun:ngx_write_channel\n   fun:ngx_pass_open_channel\n   fun:ngx_start_cache_manager_processes\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   sendmsg(msg.msg_iov[0])\n   fun:__sendmsg_nocancel\n   fun:ngx_write_channel\n   fun:ngx_pass_open_channel\n   fun:ngx_start_privileged_agent_processes\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   match-leak-kinds: definite\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_regex_malloc\n   fun:pcre2_compile_context_create_8\n   fun:ngx_regex_compile\n   fun:ngx_http_regex_compile\n   fun:ngx_http_core_regex_location\n   fun:ngx_http_core_location\n   fun:ngx_conf_handler\n   fun:ngx_conf_parse\n   fun:ngx_http_core_server\n   fun:ngx_conf_handler\n   fun:ngx_conf_parse\n   fun:ngx_http_block\n   fun:ngx_conf_handler\n   fun:ngx_conf_parse\n   fun:ngx_init_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   match-leak-kinds: definite\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_regex_malloc\n   fun:pcre2_compile_context_create_8\n   fun:ngx_regex_compile\n   fun:ngx_http_regex_compile\n   fun:ngx_http_rewrite\n   fun:ngx_conf_handler\n   fun:ngx_conf_parse\n   fun:ngx_http_core_location\n   fun:ngx_conf_handler\n   fun:ngx_conf_parse\n   fun:ngx_http_core_server\n   fun:ngx_conf_handler\n   fun:ngx_conf_parse\n   fun:ngx_http_block\n   fun:ngx_conf_handler\n   fun:ngx_conf_parse\n   fun:ngx_init_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   match-leak-kinds: definite\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_regex_malloc\n   fun:pcre2_compile_context_create_8\n   fun:ngx_regex_compile\n   fun:ngx_http_regex_compile\n   fun:ngx_http_rewrite\n   fun:ngx_conf_handler\n   fun:ngx_conf_parse\n   fun:ngx_http_core_server\n   fun:ngx_conf_handler\n   fun:ngx_conf_parse\n   fun:ngx_http_block\n   fun:ngx_conf_handler\n   fun:ngx_conf_parse\n   fun:ngx_init_cycle\n   fun:main\n}\n"
  }
]